Introduction

The Philadelphia housing market is a dynamic and diverse landscape influenced by various factors that impact both prices and overall market conditions. This market reflects the economic and social dynamics of the city and the surrounding region.It encompasses a wide range of neighborhoods, each with its unique character, amenities, and housing styles. The city’s diverse housing stock includes rowhouses, townhomes, apartments, and single-family homes, catering to a variety of lifestyles and preferences.

Significance of project

Housing prices in Philadelphia have experienced fluctuations over the years, influenced by a combination of local and national factors. However, it’s important to note that these prices can vary significantly based on factors such as the neighborhood, property type, and condition of the home.

Sale prediction algorithms are powerful tools that offer benefits to a wide range of stakeholders in the housing market. They promote informed decision-making, uncover inequalities, and contribute to the overall health and fairness of the real estate market.

This project builds on this theory and aims to evaluate the various factors affecting housing prices to create an accurate and generalizable OLS regression model to predict the housing prices in Philadelphia.

Key challenges

Building a predictive model for property valuation is difficult given the intricacies involved in data processing and interpretation. It involves sorting through lots of data, choosing the right features, understanding the nuanced influence of neighborhood characteristics, addressing collinearity among variables, selecting appropriate modeling techniques, evaluating model performance, ensuring interpretability, and building a generalizable model.

Process

The process of creating this model was extremely iterative, and involved multiple rounds of trial and error, particularly in attempts to minimize the errors in predictions. The maps, correlation matrix and scatterplots are all illustrative of variables that were considered, wrangled and filtered as part of the data analysis process.

Summary of Results

The model exhibits moderate generalizability, however, the presence of notably high errors raises concerns about its predictive accuracy. In our process of experimentation, we were able to predict approximately 70% of the variance in sale prices, which is a reasonable level of explanatory power for a regression model. The errors across the model seem to be dispersed and do not display any significant spatial auto-correlation.

This code is built upon the classwork discussed here.

R Setup and Installing Packages

This code chunk handles the essential tasks of loading necessary packages, configuring the Census API key, defining class functions, specifying a color palette, and managing global environment settings.

# Loading libraries

library(tidyverse)
library(tidycensus)
library(sf)
library(kableExtra)
library(tidyr)
library(ggplot2)
library(viridis)
library(stringr)
library(tigris)
library(ggcorrplot)
library(stargazer)
library(dplyr)
library(caTools)
library(spdep)
library(caret)
library(jtools)     
library(ggstance) 
library(ggpubr)    
library(broom.mixed) 

# Setting parameters for scientific notation

options(scipen=999)
options(tigris_class = "sf")

# Functions and data directory

source("https://raw.githubusercontent.com/urbanSpatial/Public-Policy-Analytics-Landing/master/functions.r")

# Invoking color palettes to be used

palettee <- c('#d7191c','#fdae61','#ffffbf','#abd9e9','#2c7bb6')

# Registering API Key to be used

census_api_key('bf2d507651b5a621dbadd44533fb4f3deaab26bf', overwrite = TRUE)

Data Wrangling

Census, OpenData Philly and provided Dataset

Provided Dataset and Internal Variables

The code below is used to import the foundational dataset for our model. This dataset contains information on home sales prices and property characteristics in Philadelphia, Pennsylvania for the years 2022 and 2023. The model we are developing is designed to make predictions about home prices and utilizes specific property attributes from this dataset to enhance the accuracy of those predictions.

The code demonstrates data manipulation using the dplyr package to refine the dataset by selecting specific columns and filtering rows based on criteria. These operations are carried out because the raw dataset is untidy and incomplete. The original dataset is further modified by filtering rows specific to Philadelphia City and by excluding certain columns with a majority of missing values.

# Reading Data

data <- 
  st_read("./data/studentData.geojson") %>%
  st_transform('ESRI:102286')

# Dropping columns with no values and filtering values within Philadelphia

data <-  data %>% 
  select(-cross_reference, -date_exterior_condition, -mailing_address_2, -mailing_care_of, -unfinished, -utility, -category_code, -category_code_description, -exempt_land, -separate_utilities, -sewer, - site_type, -house_extension, -street_direction, -suffix, -garage_type, -general_construction )%>% 
  filter(mailing_city_state == "PHILADELPHIA PA" )

Categorical Variables

The next step is predominantly focused on data cleaning and transformation based on the information given in the metadata for the dataset by the City of Philadelphia. Overall, this code aims to clean and categorize various columns in the dataset. It does this by handling missing values as well as assigning categories and binary values for better analysis and modeling.

## Filtering out 9 rows where year built is not given

data <-  data %>% 
  filter(year_built > 0 )

## Categorizing if a Basement is present

data <- data %>%
  mutate(BasementPresent = case_when(basements == 'A' |
  basements == 'B' |
  basements == 'C' |
  basements == 'D' |
  basements == 'E' |
  basements == 'F' |
  basements == 'G' |
  basements == 'H' |
  basements == 'I' |
  basements == 'J' ~ 1,
  basements == '0' ~ 0))

# Assigning value of 0 to 'NA' rows based on description in metadata

data$BasementPresent[is.na(data$BasementPresent)] <- 0

## Categorizing Basement Type

library(dplyr)

data <- data %>%
  mutate(BasementType = case_when(basements == 'A' ~ 'Full size finished',
  basements == 'B' ~ 'Full size semi-finished',
  basements == 'C' ~ 'Full size unfinished',
  basements == 'D' ~ 'Full size unknown finish',
  basements == 'E' ~ 'Partial size finished',
  basements == 'F' ~ 'Partial size semi-finished',
  basements == 'G' ~ 'Partial size unfinished',
  basements == 'H' ~ 'Partial size unknown finish',
  basements == 'I' ~ 'Unknown size finished',
  basements == 'J' ~ 'Unknown size unfinished',
  basements == '0' ~ 'No basement'))

# Assigning value of 0 to 'NA' rows based on description in metadata

data$BasementType[is.na(data$BasementType)] <- "No basement"
data$basements[is.na(data$basements)] <- 0

## Categorizing based on Central Air

data$central_air <- ifelse(data$central_air == 'Y', 1, 0)

## Categorizing based on Exterior Condition

data <- data %>%
  mutate(ExteriorConditionType = case_when(exterior_condition == '1' |
  exterior_condition == '2' |
  exterior_condition == '3' ~ 'Good Condition',
  exterior_condition == '4' |
  exterior_condition == '5' ~ 'Average Condition',
  exterior_condition == '6' ~ 'Below Average Condition',
  exterior_condition == '7' ~ 'Vacant and Sealed'))

# Assigning value to single 'NA' row based on 'interior_construction' rating for same row

data$ExteriorConditionType[is.na(data$ExteriorConditionType)] <- "Good Condition"
data$exterior_condition[is.na(data$exterior_condition)] <- 3

## Categorizing based on Fireplaces

# Assigning value of 0 to 'NA' rows based on description in metadata

data$fireplaces[is.na(data$fireplaces)] <- 0

## Categorizing based on Fuel Sources

data <- data %>%
  mutate(FuelType = case_when(fuel == 'A' ~ 'Natural Gas Powered',
  fuel == 'B' ~ 'Oil Powered',
  fuel == 'C' ~ 'Electric Powered',
  fuel == 'E' ~ 'Solar Powered',
  fuel == 'G' ~ 'Fuel Source Unknown'))

# Assigning value of Unknown/G to 'NA' rows based on description in metadata

data$FuelType[is.na(data$FuelType)] <- "Fuel Source Unknown"
data$fuel[is.na(data$fuel)] <- "G"

## Categorizing based on Garage Presence

data <- data %>%
  mutate(GaragePresent = case_when(garage_spaces == '0' ~ 0,
  garage_spaces == '1' |
  garage_spaces == '2' |
  garage_spaces == '3' ~ 1))

# Assigning value of 0 to 'NA' rows based on description in metadata

data$GaragePresent[is.na(data$GaragePresent)] <- 0
data$garage_spaces[is.na(data$garage_spaces)] <- 0

## Categorizing based on Garage Types

data <- data %>%
  mutate(GarageType = case_when(garage_spaces == '0' ~ 'No Garage',
  garage_spaces == '1' ~ 'Single Garage',
  garage_spaces == '2' |
  garage_spaces == '3' ~ 'Multiple Garages'))

# Assigning value of 0 to 'NA' rows based on description in metadata

data$GarageType[is.na(data$GarageType)] <- "No Garage"
data$garage_spaces[is.na(data$garage_spaces)] <- 0


data <- data %>%
  mutate(InteriorConditionType = case_when( interior_condition == '0' |
  interior_condition == '1' |
  interior_condition == '2' |
  interior_condition == '3' ~ 'Good Condition',
  interior_condition == '4' ~ 'Average Condition',
  interior_condition == '5' ~ 'Below Average Condition',
  interior_condition == '6' |
  interior_condition == '7' ~ 'Vacant and/or Sealed'))

# Assigning values to 0 or 'NA' rows based on exterior condition since interior and exterior conditions are equal in almost all cases

data$InteriorConditionType[is.na(data$InteriorConditionType)] <- c(data$ExteriorConditionType)

## Categorizing based on View Type

data <- data %>%
  mutate(ViewType = case_when(view_type == '0' ~ 'Nature of View Unknown',
  view_type == 'I' ~ 'Typical View',
  view_type == 'A' ~ 'Skyline View',
  view_type == 'B' ~ 'River View',
  view_type == 'C' ~ 'Park View',
  view_type == 'D' ~ 'Commercial Area View',
  view_type == 'E' ~ 'Industrial Area View',
  view_type == 'H' ~ 'View of Landmark'))

# Assigning value to NA rows to nature of view unknown

data$ViewType[is.na(data$ViewType)] <- "Nature of View Unknown"
data$view_type[is.na(data$view_type)] <- 0

## Categorizing based on Topography Type

data <- data %>%
  mutate(TopographyType = case_when(topography == 'A' ~ 'Above Street Level Topography',
  topography == 'B' ~ 'Below Street Level Topography',
  topography == 'C' ~ 'Flood Plain Topography',
  topography == 'D' ~ 'Rocky Topography',
  topography == 'E' ~ 'Other Topography',
  topography == 'F' ~ 'Level Topography'))

# Assigning value to NA rows to nature of view unknown

data$TopographyType[is.na(data$TopographyType)] <- "Topography Unknown"
data$topography[is.na(data$topography)] <- 0

## Categorizing based on Parcel Type

data <- data %>%
  mutate(ParcelType = case_when(parcel_shape == 'A' ~ 'Irregular Parcel',
  parcel_shape == 'B' ~ 'Grossly Irregular Parcel',
  parcel_shape == 'C' ~ 'Triangular Parcel',
  parcel_shape == 'D' ~ 'Long Narrow Parcel',
  parcel_shape == 'E' ~ 'Rectangular Parcel'))

# Assigning value to NA rows to nature of view unknown

data$ParcelType[is.na(data$ParcelType)] <- "Parcel Type Unknown"
data$parcel_shape[is.na(data$parcel_shape)] <- 0

Imputing Missing Data

An initial analysis of the dataset indicated missing values for a number of rows within important factors that inform sale prices such as number of bedrooms, number of bathrooms, and number of rooms. A linear regression model is used to predict these missing values based on the relationships observed in the available data.

In the given dataset, a strong relation is observed between number of rooms and total livable area. Here, the linear relationship between these two factors are studied across the dataset and projected to the rows with the missing values. The assumption here is that, when plotted on a graph, the missing values will have the same slope and intercept as the rest of the dataset.

This imputing practice is extended to other fields with missing values such as number of bathrooms and number of bedrooms.

Bedrooms

## Imputing values for missing values of number of bedrooms based on total livable area

# Dropping 32 values of total livable area which are 0 for better prediction

data <-  data %>% 
  filter(total_livable_area > 0 )

# Step 1 - Creating an index of 0 and 1 values for rows that have values for number of bathrooms and rows that do not

data <- data %>%
  mutate(BedroomIndex = case_when(number_of_bedrooms >= 1 ~ 1,
                               number_of_bedrooms < 1 ~ 0))
                               
data$BedroomIndex[is.na(data$BedroomIndex)] <- 0

# Step 2 - Creating a linear regression model relating number of bedrooms and total liveable area

lm(number_of_bedrooms ~ total_livable_area, data=data)

# Step 3 - Imputing new values for missing values of number of bedrooms using regression results

for(i in 1:nrow(data))
{
  if (data$BedroomIndex[i] == 0)
  {
    data$number_of_bedrooms[i] = 2.1605650 + 0.0003057*data$total_livable_area[i]
  }
  }

data$number_of_bedrooms <- round(data$number_of_bedrooms, digits=0)

Rooms

## Imputing values for missing values of number of rooms based on total livable area

# Step 1 - Creating an index of 0 and 1 values for rows that have values for number of rooms and rows that do not

data <- data %>%
  mutate(RoomIndex = case_when(number_of_rooms >= 1 ~ 1,
                               number_of_rooms < 1 ~ 0))
                               
data$RoomIndex[is.na(data$RoomIndex)] <- 0

# Step 2 - Creating a linear regression model relating number of rooms and total livable area

lm(number_of_rooms ~ total_livable_area, data=data)

# Step 3 - Imputing new values for missing values of number of rooms using regression results

for(i in 1:nrow(data))
{
  if (data$RoomIndex[i] == 0)
  {
    data$number_of_rooms[i] = 4.316503 + 0.001319*data$total_livable_area[i]
  }
  }

data$number_of_rooms <- round(data$number_of_rooms, digits=0)

Bathrooms

## Imputing values for missing values of number of bathrooms based on total livable area

# Step 1 - Creating an index of 0 and 1 values for rows that have values for number of bathrooms and rows that do not

data <- data %>%
  mutate(BathroomIndex = case_when(number_of_bathrooms >= 1 ~ 1,
                               number_of_bathrooms < 1 ~ 0))

data$BathroomIndex[is.na(data$BathroomIndex)] <- 0

# Step 2 - Creating a linear regression model relating number of bathrooms and  total livable area

lm(number_of_bathrooms ~ total_livable_area, data=data)

# Step 3 - Imputing new values for missing values of number of bathrooms using regression results

for(i in 1:nrow(data))
{
  if (data$BathroomIndex[i] == 0)
  {
    data$number_of_bathrooms[i] = 0.6426310 + 0.0003283*data$total_livable_area[i]
  }
  }

data$number_of_bathrooms <- round(data$number_of_bathrooms, digits=0)

Creating New Variables

Post cleaning, two new variables are created within the dataset to evaluate the price per square foot within sold properties as well as the age of the sold properties.

data <- 
  data %>%
  mutate(PricePerSqft = (sale_price/total_livable_area))

data <- 
  data %>%
  mutate(BuildingAge = (2023 - (year_built)))

Dropping Columns and Outliers

As the last step, the foundational dataset carrying information about the properties is filtered to only hold the indicative internal variables which may play a role in influencing the home price value. To achieve better accuracy and fewer errors in prediction, outlier values in the dataset are eliminated to remove any significant skews in the dataset.

data <- data %>% 
  select(objectid, assessment_date, BuildingAge, year_built, building_code, building_code_description, pin, building_code_new, building_code_description_new,  census_tract, geographic_ward, zoning, location, street_name, street_code, street_designation, zip_code, house_number, depth, frontage, central_air, fireplaces, fuel, FuelType, basements, BasementPresent, BasementType, garage_spaces, GaragePresent, GarageType, exterior_condition, ExteriorConditionType, interior_condition, InteriorConditionType, number_of_bathrooms, number_of_bedrooms, number_of_rooms, number_stories, total_livable_area, view_type, ViewType, topography, TopographyType, parcel_shape, ParcelType, sale_date, sale_year, sale_price, PricePerSqft, musaID, toPredict, geometry )
data <-  data %>% 
  filter(number_of_bedrooms <10, number_of_rooms<15, ((number_of_bathrooms+number_of_bedrooms) < number_of_rooms), PricePerSqft<1500, total_livable_area<10000)

Census Data

After cleaning the primary dataset and importing data on surrounding physical amenities, the next step is gathering relevant census data to understand demographic conditions pertaining to these data points. The provided R code chunk fetches and processes data from the American Community Survey (ACS) for Philadelphia census tracts in the year 2021. It retrieves various demographic and socioeconomic variables for these tracts and performs some data transformations. The year chosen for analysis is 2021 to factor recovery from Covid in order to make more accurate predictions.

A list of variables including population, income, housing-related information, education, poverty levels, and racial/ethnic demographics are gathered and transformed to be projected in the ESRI:102728 coordinate system. This information is stored in a new dataset - ‘tracts21’.

acs_variable_list.2021 <- load_variables(2021, #year
                                         "acs5", #five year ACS estimates
                                         cache = TRUE)
# 2021, A

# Retrieve ACS data for Philadelphia tracts in 2020
tracts21 <- get_acs(
  geography = "tract",
  variables = c(
    "B01003_001",   # Total Population
    "B19013_001",   # Median Household Income
    "B25058_001",   # Median Rent
    "B25008_002",   # Owner-Occupied Units
    "B25008_003",   # Renter-Occupied Units
    "B07001_032",   # Same House 75 Years Ago
    "B07001_017",   # Same House 1 Year Ago
    "B25088_003",   # Median Selected Monthly Owner Costs (homes without a mortgage)
    "B25088_002",   # Median Selected Monthly Owner Costs (homes with a mortgage)
    "B25064_001",   # Median Gross Rent (rent and utilities)
    "B25117_001",   # Percentage of Housing Units with heat
    "B15003_022",   # Educational Attainment: Bachelor's Degree
    "B17001_002",   # Percentage of Population Below the Poverty Level
    "B28002_004",   # Percentage of Housing Units with High-Speed Internet
    "B25044_003",   # Percentage of Housing Units with No Vehicle Available
    "B02001_002",   # Race and Ethnicity: White Alone
    "B02001_003",   # Race and Ethnicity: Black or African American Alone
    "B03001_003"    # Hispanic or Latino Origin of Population
  ),
  year = 2021,
  state = "PA",
  county = "Philadelphia",
  geometry = TRUE,
  output = "wide"
)%>%
  select(-NAME, -ends_with("M")) %>%
  rename(totalpop = B01003_001E,                           # Total Population
         med_income = B19013_001E,                         # Median Household Income
         med_rent = B25058_001E,                           # Median Rent
         owner_units = B25008_002E,                        # Owner-Occupied Units
         renter_units = B25008_003E,                       # Renter-Occupied Units
         same_house_75 = B07001_032E,                      # Same House 75 Years Ago
         same_house_1 = B07001_017E,                       # Same House 1 Year Ago
         monthly_costs_no_mortgage = B25088_003E,          # Median Selected Monthly Owner Costs (homes without a mortgage)
         monthly_costs_with_mortgage = B25088_002E,        # Median Selected Monthly Owner Costs (homes with a mortgage)
         med_gross_rent = B25064_001E,                     # Median Gross Rent (rent and utilities)
         housing_units_with_heat = B25117_001E,            # Percentage of Housing Units with heat
         edu_bachelors = B15003_022E,                      # Educational Attainment: Bachelor's Degree
         pop_below_poverty = B17001_002E,                  # Percentage of Population Below the Poverty Level
         housing_units_high_speed_internet = B28002_004E,  # Percentage of Housing Units with High-Speed Internet
         housing_units_no_vehicle = B25044_003E,           # Percentage of Housing Units with No Vehicle Available
         race_white = B02001_002E,                         # Race and Ethnicity: White Alone
         race_black = B02001_003E,                         # Race and Ethnicity: Black or African American Alone
         hispanic_latino = B03001_003E                     # Race and Ethnicity: Hispanic or Latino
         )

# Transform the data to ESRI:102728 projection
tracts21 <- tracts21 %>% st_transform(st_crs(data))

Open Data philly

To begin constructing a predictive model for the Philadelphia housing market, additional external variables are incorporated. These variables examine the proximity of various amenities and public services to the homes listed in the foundational dataset. The following code imports data related to these potentially influential amenities, including information about the locations of public and private schools, colleges, city landmarks, playgrounds, trails, tennis courts, pools, transit stops and police stations. Additionally, information on potential home value inhibitors such as litter, proximity to the floodplain and instances of gun violence are also brought in. These datasets are sourced from Open Data Philly.

# Nearest Schools
## Adding data on Philadelphia Schools

PhillySchools <-
   st_read("./data/Schools.geojson") %>%
  st_transform(st_crs(tracts21))

PhillyPvtSchools <-
   st_read("./data/Schools.geojson") %>%
  filter(TYPE_SPECIFIC == "PRIVATE") %>%
  st_transform(st_crs(tracts21))

## Mapping nearest school

nearest_school <- sf::st_nearest_feature(data, PhillySchools)
nearest_pvt_school <- sf::st_nearest_feature(data, PhillyPvtSchools)

## Converting schools to rsgeo geometries

x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillySchools)

## Calculating distance

data$dist_to_nearest_school <- rsgeo::distance_euclidean_pairwise(x, y[nearest_school])

## Converting private schools to rsgeo geometries

x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillyPvtSchools)

## Calculating distance

data$dist_to_nearest_pvt_school <- rsgeo::distance_euclidean_pairwise(x, y[nearest_pvt_school])

# Nearest Colleges
## Adding data on Philadelphia Colleges

PhillyColleges <-
 st_read("./data/Universities_Colleges.geojson")%>%
  st_transform(st_crs(tracts21))

## Mapping nearest college

nearest_college <- sf::st_nearest_feature(data, PhillyColleges)

## Converting to rsgeo geometries

x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillyColleges)

## Calculating distance

data$dist_to_nearest_college <- rsgeo::distance_euclidean_pairwise(x, y[nearest_college])

# Nearest Landmarks
## Adding data on Philadelphia landmarks 

PhillyLandmarks <-
 st_read("https://services.arcgis.com/fLeGjb7u4uXqeF9q/arcgis/rest/services/Landmark_Points/FeatureServer/0/query?outFields=*&where=1%3D1&f=geojson")%>%
  st_transform(st_crs(tracts21))

## Mapping nearest landmark

nearest_landmark <- sf::st_nearest_feature(data, PhillyLandmarks)

## Converting to rsgeo geometries

x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillyLandmarks)

## Calculating distance

data$dist_to_nearest_landmark <- rsgeo::distance_euclidean_pairwise(x, y[nearest_landmark])

# Nearest Commercial Corridors
## Adding data on Philadelphia Commercial Corridors 

PhillyComCorr <-
  st_read("./data/Commercial_Corridors.geojson") %>%
  st_transform(st_crs(tracts21))

## Is it within the commercial corridor?

data$within_com_corr <- ifelse(st_within(data, PhillyComCorr), 1, 0)

data <- data %>%
  mutate(within_com_corr = ifelse(is.na(within_com_corr), 0, 1))

## Mapping nearest commercial corridor

nearest_corridor <- sf::st_nearest_feature(data, PhillyComCorr)

## Converting to rsgeo geometries

x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillyComCorr)

## Calculating distance

data$dist_to_comm_corr <- rsgeo::distance_euclidean_pairwise(x, y[nearest_corridor])

# Litter Index
## Adding data on Philadelphia's Litter Index

PhillyLitter <-
  st_read("./data/Litter_Index.geojson") %>%
  st_transform(st_crs(tracts21))

## Joining the litter score

data <- 
 st_join(data,(PhillyLitter %>%
          select(-OBJECTID, -Shape__Area, -Shape__Length )%>%
          rename(litter = SCORE))) 

#Nearest Flood Plains
## Adding data on Philadelphia's Flood Plain

PhillyFlood <- 
  st_read("./data/FEMA_100_flood_Plain.geojson") %>%
  st_transform(st_crs(tracts21))

## Is it within the floodplain?

data$within_flood <- ifelse(st_within(data, PhillyFlood), 1, 0)

data <- data %>%
  mutate(within_flood = ifelse(is.na(within_flood), 0, 1))

## Mapping nearest floodplain 

nearest_floodplain <- sf::st_nearest_feature(data, PhillyFlood)

## Converting to rsgeo geometries

x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillyFlood)

## Calculating distance

data$dist_to_floodplain <- rsgeo::distance_euclidean_pairwise(x, y[nearest_floodplain])

# Nearest Transit Stops
## Adding data on Philadelphia's Transit Stops

el <- st_read("https://opendata.arcgis.com/datasets/8c6e2575c8ad46eb887e6bb35825e1a6_0.geojson")
Broad_St <- st_read("https://opendata.arcgis.com/datasets/2e9037fd5bef406488ffe5bb67d21312_0.geojson")

PhillySeptaStops <- 
  rbind(
     el %>% 
      mutate(Line = "El") %>%
      dplyr::select(Station, Line),
     Broad_St %>%
      mutate(Line ="Broad_St") %>%
      dplyr::select(Station, Line)) %>%
  st_transform(st_crs(tracts21))  

## Mapping nearest transit stop

nearest_station <- sf::st_nearest_feature(data, PhillySeptaStops)

## Converting to rsgeo geometries

x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillySeptaStops)

## Calculating distance

data$dist_to_nearest_station <- rsgeo::distance_euclidean_pairwise(x, y[nearest_station])

# Nearest Trails
## Adding data on Philadelphia Trails 

PhillyTrails <-
 st_read("./data/PPR_Trails.geojson")%>%
  st_transform(st_crs(tracts21))

## Mapping nearest trail

nearest_trail <- sf::st_nearest_feature(data, PhillyTrails)

## Converting to rsgeo geometries

x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillyTrails)

## Calculating distance

data$dist_to_nearest_trail <- rsgeo::distance_euclidean_pairwise(x, y[nearest_trail])

# Nearest Tennis Courts
## Adding data on Philadelphia Tennis Courts 

PhillyTennisCourts <-
 st_read("./data/PPR_Tennis_Courts.geojson")%>%
  st_transform(st_crs(tracts21))

## Mapping nearest tennis court

nearest_tenniscourt <- sf::st_nearest_feature(data, PhillyTennisCourts)

## Converting to rsgeo geometries

x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillyTennisCourts)

## Calculating distance

data$dist_to_nearest_tenniscourt <- rsgeo::distance_euclidean_pairwise(x, y[nearest_tenniscourt])

# Nearest Playgrounds
## Adding data on Philadelphia Playgrounds 

PhillyPlaygrounds <-
 st_read("./data/PPR_Playgrounds.geojson")%>%
  st_transform(st_crs(tracts21))

## Mapping nearest playground

nearest_playground <- sf::st_nearest_feature(data, PhillyPlaygrounds)

## Converting to rsgeo geometries

x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillyPlaygrounds)

## Calculating distance

data$dist_to_nearest_playground <- rsgeo::distance_euclidean_pairwise(x, y[nearest_playground])

# Nearest Pools
## Adding data on Philadelphia Pools 

PhillyPools <-
 st_read("./data/PPR_Swimming_Pools.geojson")%>%
  st_transform(st_crs(tracts21))

## Mapping nearest pool

nearest_pool <- sf::st_nearest_feature(data, PhillyPools)

## Converting to rsgeo geometries

x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillyPools)

## Calculating distance

data$dist_to_nearest_pool <- rsgeo::distance_euclidean_pairwise(x, y[nearest_pool])

# Nearest Police Stations
## Adding data on Philadelphia Police Stations

PhillyPoliceStations <-
 st_read("./data/Police_Stations.geojson")%>%
  st_transform(st_crs(tracts21))

## Mapping nearest police station

nearest_police_station <- sf::st_nearest_feature(data, PhillyPoliceStations)

## Converting to rsgeo geometries

x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillyPoliceStations)

## Calculating distance

data$dist_to_nearest_police_station <- rsgeo::distance_euclidean_pairwise(x, y[nearest_police_station])

A distance analysis is performed for each of the above amenities in relation to our home value dataset to identify the nearest amenity of each kind to each house in the original dataset. This analysis is conducted as a preparatory step, the distances derived from this analysis are studied next to determine if these spatial factors play a role in influencing home prices.

K Nearest Neighbors

In order to evaluate the instances of gun violence around a listed home, the method of k-Nearest Neighbors Analysis is used. In this case, a higher value of k=5 is preferred to account for the spatial distribution of the instances in relation to the homes and to ensure that there are enough neighbor observations for each listed home for better balance across the dataset. A higher k value is also preferred for the robustness and higher generalizability it offers during prediction.

# Philadelphia Shootings
## Adding data on Philadelphia Shootings

PhillyShootings <-
 st_read("./data/shootings2021.geojson")%>%
  dplyr::select("date_", "point_x", "point_y", "geometry")%>%
  st_transform(st_crs(data))%>%
  na.omit() 
## Reading layer `shootings2021' from data source 
##   `D:\Fall_2023\PPA\Midterm\data\shootings2021.geojson' using driver `GeoJSON'
## replacing null geometries with empty geometries
## Simple feature collection with 2341 features and 21 fields (with 11 geometries empty)
## Geometry type: POINT
## Dimension:     XY
## Bounding box:  xmin: -75.26654 ymin: 39.90286 xmax: -74.95936 ymax: 40.1173
## Geodetic CRS:  WGS 84
##calculating distance to nearest neighbors

library(sf)
library(spatstat)
library(class)

data_ppp <- as.ppp(data)
PhillyShootings_ppp <- as.ppp(PhillyShootings)

data$crime_nn5 <- nncross(data_ppp, PhillyShootings_ppp, k = 5)
tracts21 <- 
  tracts21 %>%
  mutate(PctWhite = ((race_white/totalpop)*100),
         PctBlack = ((race_black/totalpop)*100),
         PctHispanic = ((hispanic_latino/totalpop)*100),
         PctBachelors = ((edu_bachelors/totalpop)*100),
         PctPoverty = ((pop_below_poverty/totalpop)*100))

Joining Data

In order to begin conducting exploratory analyses of the relations within the dataset, the foundational dataset is joined with the datasets containing relevant spatial and demographic attributes.

# joining census data

data <- 
  st_join(data, tracts21)
#saving for prediction 

data_og <- data

data <- data %>%
  filter(sale_price > 0) 

Exploratory Data Analysis

Provided below are the summary statistics for the different types of variables considered (internal, spatial, and demographic). The exploration further looks at the distribution of non-numeric categorical variables through bar plots. It finally inspects the dependence of ‘home sale price’ on other continuous independent variables including total area, median income, and racial distribution among others.

Summary Statistics: Internal Variables

These statistics provide an overview of the central tendency, spread, and range of the internal variables for Philadelphia homes, which include sale prices, price per square foot, living area size, year built, number of rooms, number of bathrooms, and number of bedrooms.

InternalVariables <- data 

InternalVariables <- st_drop_geometry(InternalVariables)

InternalVariables <- InternalVariables %>%
  dplyr::select("sale_price", "PricePerSqft", "total_livable_area", "year_built", "number_of_rooms", "number_of_bathrooms", "number_of_bedrooms") 

stargazer(as.data.frame(InternalVariables), type="text", digits=1, title = "Descriptive Statistics for Philadelphia Homes Internal Variables (Figure 4.1)", out = "Training_PHLInternal.txt")
## 
## Descriptive Statistics for Philadelphia Homes Internal Variables (Figure 4.1)
## ===============================================================
## Statistic             N      Mean    St. Dev.   Min      Max   
## ---------------------------------------------------------------
## sale_price          18,040 282,857.8 228,937.6 11,000 5,900,000
## PricePerSqft        18,040   209.3     118.7    7.2    1,443.2 
## total_livable_area  18,040  1,334.0    516.8    186     7,391  
## year_built          18,040  1,937.8    29.8    1,750    2,024  
## number_of_rooms     18,040    6.1       0.7      3       14    
## number_of_bathrooms 18,040    1.2       0.4      1        5    
## number_of_bedrooms  18,040    2.9       0.6      1        8    
## ---------------------------------------------------------------

It is observed that the average sale price of homes in Philadelphia is approximately 282,857.8 USD. The sale prices vary quite widely with a high standard deviation, indicating significant dispersion in prices. It is also observed that the average livable area in Philadelphia is approximately 1334 sqft with the price per square foot for homes positioned at about 209.3 USD.

The average age for a house in Philadelphia is approximately 85 years indicating a large share of the market is held by homes constructed over a century ago.

Finally, homes in Philadelphia on average seem to have 6 rooms overall with approximately 3 bedrooms and 1 bathroom.

Summary Statistics: Demographic Variables

These statistics provide an overview of the central tendency, spread, and range of the internal variables for Philadelphia homes, which include median income, percentage of white population, percentage of black population, percentage of hispanic population, percentage of population with a bachelor’s degree and percentage of population living in poverty.

DemographicVariables <- data 

DemographicVariables <- st_drop_geometry(DemographicVariables)

DemographicVariables <- DemographicVariables %>%
  dplyr::select("PctWhite", "PctBlack", "PctHispanic", "PctBachelors", "PctPoverty", "med_income") 

stargazer(as.data.frame(DemographicVariables), type="text", digits=1, title = "Descriptive Statistics for Philadelphia Homes Demographic Variables (Figure 4.1)", out = "Training_PHLSpatial.txt")
## 
## Descriptive Statistics for Philadelphia Homes Demographic Variables (Figure 4.1)
## ====================================================
## Statistic      N      Mean   St. Dev.  Min     Max  
## ----------------------------------------------------
## PctWhite     18,039   44.1     30.6    0.0    95.7  
## PctBlack     18,039   35.7     33.2    0.0    98.9  
## PctHispanic  18,039   15.4     18.8    0.0    91.7  
## PctBachelors 18,039   14.1     10.0    0.0    42.3  
## PctPoverty   18,039   20.4     13.1    0.0    65.1  
## med_income   17,882 61,320.0 28,995.2 11,955 210,322
## ----------------------------------------------------

The table presents descriptive statistics for demographic variables related to Philadelphia homes, offering insights into the characteristics of the population in different areas.

On average, approximately 44.1% of the population in the observed areas is White. The range of White population percentages spans from 0.0% to 95.7%, indicating a wide diversity in racial composition across different areas of Philadelphia. The same holds true for Black and Hispanic population percentages indicating the presence of White-majority, Black majority and Hispanic majority neighborhoods in different parts of Philadelphia. On average, about 14.1% of the population in the observed areas holds a Bachelor’s degree or higher.

The average median household income in the observed areas is $61,320.0. However, approximately 20.4% of the population in the observed areas falls below the poverty level, on average.

Summary Statistics: Spatial Variables

These statistics provide an overview of the central tendency, spread, and range of the internal variables for Philadelphia homes, which include distances to key nearest amenities and threatening activities including nearest schools, nearest private schools, nearest landmarks, nearest commercial corridors, nearest trails, nearest police stations, and nearest crime instances.

SpatialVariables <- data 

SpatialVariables <- st_drop_geometry(SpatialVariables)

SpatialVariables <- SpatialVariables %>%
  dplyr::select("dist_to_nearest_school", "dist_to_nearest_pvt_school", "dist_to_nearest_landmark", "dist_to_comm_corr", "dist_to_nearest_station", "dist_to_nearest_trail", "dist_to_nearest_police_station","crime_nn5") 

stargazer(as.data.frame(SpatialVariables), type="text", digits=1, title = "Descriptive Statistics for Philadelphia Homes Spatial Variables (Figure 4.1)", out = "Training_PHLSpatial.txt")
## 
## Descriptive Statistics for Philadelphia Homes Spatial Variables (Figure 4.1)
## ====================================================================
## Statistic                        N     Mean   St. Dev. Min    Max   
## --------------------------------------------------------------------
## dist_to_nearest_school         18,040  351.4   202.0   9.9  1,799.4 
## dist_to_nearest_pvt_school     18,040  859.3   555.4   24.4 3,784.1 
## dist_to_nearest_landmark       18,040  441.6   259.8   5.8  2,213.8 
## dist_to_comm_corr              18,040  207.7   201.7   0.0  2,252.1 
## dist_to_nearest_station        18,040 2,683.8 2,809.9  35.9 13,808.6
## dist_to_nearest_trail          18,040 1,403.1  927.2   9.6  4,993.5 
## dist_to_nearest_police_station 18,040 1,652.1  959.3   29.4 5,378.6 
## --------------------------------------------------------------------

These statistics offer insights into the spatial characteristics of Philadelphia neighborhoods where the “Mean” in each case represents the average distance to a specific amenity from a listed property.

Plotting Categorical Variables

The following exploratory analysis plot, titled “Price as a function of Categorical Variables,” is a set of grouped bar charts. Each chart corresponds to a selected categorical variable, and within each chart, different categories of that variable are compared based on their influence on the sale price of homes. This visualization helps assess how each categorical variable relates to home sale prices and allows for visual comparison across categories. This allows a clear comparison of multiple categorical variables at once, making it easier to identify trends and patterns in the data.

CategoricalVariables <- data 
CategoricalVariables <- st_drop_geometry(CategoricalVariables)
CategoricalVariables <- CategoricalVariables %>%
  dplyr::select("BasementType", "GarageType", "ViewType", "ExteriorConditionType", "InteriorConditionType", "building_code_description_new","sale_price") 

CategoricalVariables %>%
  gather(CategoricalVariables, Value, -sale_price) %>%
  ggplot(aes(Value, sale_price)) +
  geom_bar(stat = "identity", position = "dodge") +
  facet_wrap(~CategoricalVariables, scales = "free", ncol=2) +
  labs(title = "Price as a function of Categorical Variables") +
  plotTheme() + theme(axis.text.x = element_text(angle = 45, hjust = 1))

Exploratory Mapping and Analysis

Mapping Internal Variables

##Mapping Internal Variables
# Mapping sale price

ggplot() +
  geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
  geom_sf(data = data, aes(colour = q5(sale_price)), 
          show.legend = "point", size = .75) +
  scale_colour_manual(values = palettee,
                   labels=qBr(data, "sale_price"),
                   name="Quintile Breaks:\n Sale Price", 
                   na.value = NA) +
  labs(title="Properties by Sale Price", subtitle = "Philadelphia 2022-2023", 
      caption="Figure 3.1.1") +
  mapTheme()

# Mapping Size

ggplot() +
  geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
  geom_sf(data = data, aes(colour = q5(total_livable_area)), 
          show.legend = "point", size = .75) +
  scale_colour_manual(values = palettee,
                   labels=qBr(data, "total_livable_area"),
                   name="Quintile Breaks:\nArea in Sq Ft", 
                   na.value = NA) +
  labs(title="Properties by Size", subtitle = "Philadelphia 2022-2023", 
      caption="Figure 3.1.2") +
  mapTheme()

# Mapping Interior Condition

ggplot() +
  geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
  geom_sf(data = data, aes(colour = (InteriorConditionType)), 
          show.legend = "point", size = .5) +
  scale_colour_manual(values = palettee, name="Interior Condition",
                   na.value = NA) +
  labs(title="Properties by Internal Condition", subtitle = "Philadelphia 2022-2023", 
      caption="Figure 3.1.3") +
  mapTheme()

Mapping Spatial Variables

## Mapping Spatial Variables

# Mapping properties around schools

ggplot() +
  geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
  geom_sf(data = data, aes(colour = q5(sale_price)), 
          show.legend = "point", size = .75, alpha=0.3) + 
  geom_sf(data = PhillySchools, colour = "black", size = 1.5, alpha=0.6) +
  scale_colour_manual(values = palettee,
                   labels=qBr(data, "sale_price"),
                   name="Quintile Breaks:\nSale Prices", 
                   na.value = NA) + 
  labs(title="Properties Around Schools", subtitle = "Philadelphia 2022-2023", 
      caption="Figure 3.2.1") +
  mapTheme()

# Mapping properties around commercial corridors

ggplot() +
  geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
  geom_sf(data = data, aes(colour = q5(sale_price)), 
          show.legend = "point", size = .75, alpha=0.3) + 
  geom_sf(data = PhillyComCorr, colour = "black", fill="black", alpha=0.6) +
  scale_colour_manual(values = palettee,
                   labels=qBr(data, "sale_price"),
                   name="Quintile Breaks:\nSale Prices", 
                   na.value = NA) + 
  labs(title="Properties Around Commercial Corridors", subtitle = "Philadelphia 2022-2023", 
      caption="Figure 3.2.2") +
  mapTheme()

# Mapping properties around landmarks

ggplot() +
  geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
  geom_sf(data = data, aes(colour = q5(sale_price)), 
          show.legend = "point", size = .75, alpha=0.3) + 
  geom_sf(data = PhillyLandmarks, colour = "black", fill="black", size = .75, alpha=0.6) +
  scale_colour_manual(values = palettee,
                   labels=qBr(data, "sale_price"),
                   name="Quintile Breaks:\nSale Prices", 
                   na.value = NA) + 
  labs(title="Properties Around Landmarks", subtitle = "Philadelphia 2022-2023", 
      caption="Figure 3.2.3") +
  mapTheme()

# Mapping properties around floodplains

ggplot() +
  geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
  geom_sf(data = data, aes(colour = q5(sale_price)), 
          show.legend = "point", size = .75, alpha=0.3) + 
  geom_sf(data = PhillyFlood, colour = "black", fill="black", size = .75, alpha=0.6) +
  scale_colour_manual(values = palettee,
                   labels=qBr(data, "sale_price"),
                   name="Quintile Breaks:\nSale Prices", 
                   na.value = NA) + 
  labs(title="Properties Around Flood Plain", subtitle = "Philadelphia 2022-2023", 
      caption="Figure 3.2.4") +
  mapTheme()

#library(gridExtra)

#grid.arrange(
  #b1,
  #b2,
  #b3,
  #b4,
  #nrow = 2,
  #widths=c(4,4),
  #top = "Title of the page"
  #)

Mapping Demographic Variables

## Mapping Demographic Variables

# Mapping median income around sold properties

ggplot() +
  geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
  geom_sf(data = tracts21, aes(fill = q5(med_income)), color = "transparent", alpha=0.5) +
    scale_fill_brewer(palette = "BuPu",
                      labels = qBr(tracts21, "med_income"),
                      name = "Median Income\nQuintile Breaks") +
  geom_sf(data = data, aes(colour = q5(sale_price)), 
          show.legend = "point", size = .2) +
  scale_colour_manual(values = palettee,
                   labels=qBr(data, "sale_price"),
                   name="Quintile Breaks:\nSale Prices", 
                   na.value = NA) + 
  labs(title="Median Income Around Sold Properties", subtitle = "Philadelphia 2022-2023", 
      caption="Figure 3.3.1")+
  mapTheme()

# Mapping white population around sold properties

ggplot() +
  geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
  geom_sf(data = tracts21, aes(fill = q5(PctWhite)), color = "transparent", alpha=0.5) +
    scale_fill_brewer(palette = "BuPu",
                      labels = qBr(tracts21, "PctWhite"),
                      name = "% White Population\nQuintile Breaks") +
  geom_sf(data = data, aes(colour = q5(sale_price)), 
          show.legend = "point", size = .2) +
  scale_colour_manual(values = palettee,
                   labels=qBr(data, "sale_price"),
                   name="Quintile Breaks:\nSale Prices", 
                   na.value = NA) + 
  labs(title="% White Population Around Sold Properties", subtitle = "Philadelphia 2022-2023", 
      caption="Figure 3.3.2")+
  mapTheme()

# Mapping black population around sold properties

ggplot() +
  geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
  geom_sf(data = tracts21, aes(fill = q5(PctBlack)), color = "transparent", alpha=0.5) +
    scale_fill_brewer(palette = "BuPu",
                      labels = qBr(tracts21, "PctBlack"),
                      name = "% Black Population\nQuintile Breaks") +
  geom_sf(data = data, aes(colour = q5(sale_price)), 
          show.legend = "point", size = .2) +
  scale_colour_manual(values = palettee,
                   labels=qBr(data, "sale_price"),
                   name="Quintile Breaks:\nSale Prices", 
                   na.value = NA) + 
  labs(title="% Black Population Around Sold Properties", subtitle = "Philadelphia 2022-2023", 
      caption="Figure 3.3.3")+
  mapTheme()

# Mapping population with bachelor's degree around sold properties

ggplot() +
  geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
  geom_sf(data = tracts21, aes(fill = q5(PctBachelors)), color = "transparent", alpha=0.5) +
    scale_fill_brewer(palette = "BuPu",
                      labels = qBr(tracts21, "PctBachelors"),
                      name = "% Bachelor's Degree\nQuintile Breaks") +
  geom_sf(data = data, aes(colour = q5(sale_price)), 
          show.legend = "point", size = .2) +
  scale_colour_manual(values = palettee,
                   labels=qBr(data, "sale_price"),
                   name="Quintile Breaks:\nSale Prices", 
                   na.value = NA) + 
  labs(title="% Population with Bachelor's Degree Around Sold Properties", subtitle = "Philadelphia 2022-2023", 
      caption="Figure 3.3.4")+
  mapTheme()

# Mapping population in poverty around sold properties

ggplot() +
  geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
  geom_sf(data = tracts21, aes(fill = q5(PctPoverty)), color = "transparent", alpha=0.5) +
    scale_fill_brewer(palette = "BuPu",
                      labels = qBr(tracts21, "PctPoverty"),
                      name = "% Pop in Poverty\nQuintile Breaks") +
  geom_sf(data = data, aes(colour = q5(sale_price)), 
          show.legend = "point", size = .2) +
  scale_colour_manual(values = palettee,
                   labels=qBr(data, "sale_price"),
                   name="Quintile Breaks:\nSale Prices", 
                   na.value = NA) + 
  labs(title="% Population in Poverty Around Sold Properties", subtitle = "Philadelphia 2022-2023", 
      caption="Figure 3.3.5")+
  mapTheme()

Scatterplots and Correlations

Scatterplots

The following code generates a set of scatterplots to explore the relationships between the sale price of homes and various continuous variables. The data is first filtered to exclude observations where the sale_price is less than 10,000,000 to remove any outliers. For the remaining data, a set of scatterplots is created. Each scatterplot examines how the sale_price relates to different continuous variables. The scatterplots help visualize the relationships between the sale price of homes and each of the continuous variables, as well as the direction and strength of these relationships. The regression lines indicate whether there is a linear trend in the data.

Correlations

The process of variable selection for the model was an iterative one, beginning with an initial exploration of inter-variable relationships. Following our strategy of categorizing variables into three overarching groups—Internal, Spatial, and Demographic—we proceeded to evaluate and visualize the correlations between these variables and the sale_price. Broadly speaking, we observed that internal and demographic variables exhibited the most pronounced correlations with sale_price, with only a limited number of spatial variables demonstrating noteworthy associations with the independent variables sale_price.

Correlation Matrix: Internal Variables

For this step, a list of important internal variables for real estate, like sale price, property size, and various features, is defined in the ‘internalvariables’ list. The code then processes the data, picking out only the numbers from these internal variables and removing any rows with missing data. After that, it calculates how these internal variables are related to each other using a correlation matrix.

Upon analyzing the results, we observe that variables such as price per square foot, number of rooms, and total livable area exhibit strong correlations with the sale price. However, it’s essential to keep in mind that the price per square foot is derived from the sale price, which explains the high correlation. Similarly, for the number of rooms and number of bedrooms, considering the imputations made earlier in this analysis, it becomes a questionable choice to include them in the final regression analysis due to the high number of missing values and the general uncertainty surrounding the assigned values, especially given the high correlation with another variable, total_living_area, which is already included in the analysis.

The final internal variables selected for this analysis include: total_livable_area,number_of_bathrooms, fireplaces, exterior_condition, and central_air.

internalvariables <- c(
  "sale_price", "PricePerSqft", "total_livable_area", "number_of_rooms", "number_of_bathrooms", "number_of_bedrooms", "BuildingAge", "frontage", "depth", "fireplaces", "BasementPresent", "GaragePresent", "exterior_condition", "number_stories", "central_air"
)

numericintVars <- data %>%
  st_drop_geometry(data) %>%
  select(internalvariables)%>%
  na.omit()

ggcorrplot(
  round(cor(numericintVars), 1), 
  p.mat = cor_pmat(numericintVars),
  colors = c('#d7191c','#ffffbf','#2c7bb6'),
  type="lower",
  insig = "blank") +  
    labs(title = "Correlation across Internal Variables") 

Correlation Matrix: Spatial Variables

Unexpectedly, during our analysis, we discovered that there were relatively few strong positive or negative correlations among the spatial elements we examined. Unlike the demographics and internal variables, which demonstrated significant influence on the sale price, these spatial factors seemed to have less impact. Despite our best efforts and attempts to be innovative in our variable selection, the overall correlations appeared to be less pronounced in this context. This observation might also be linked to the absence of error clustering that we identified later in our analysis. If time was no contraint, we would have explored other spatial varibles further, hoping to find more statistically significant ones.

spatvariables <- c("sale_price", "dist_to_nearest_school", "dist_to_nearest_pvt_school", "dist_to_nearest_landmark", "dist_to_comm_corr", "dist_to_nearest_station", "dist_to_nearest_trail", "dist_to_nearest_police_station","crime_nn5", "litter", "within_com_corr", "within_flood", "dist_to_floodplain", "dist_to_nearest_tenniscourt", "dist_to_nearest_playground", "dist_to_nearest_pool"  
  )

numericspatVars <- data %>%
  st_drop_geometry(data) %>%
  select(all_of(spatvariables))%>%
  na.omit()

ggcorrplot(
  round(cor(numericspatVars), 1), 
  p.mat = cor_pmat(numericspatVars),
  colors = c('#d7191c','#ffffbf','#2c7bb6'),
  type="lower",
  insig = "blank") +  
    labs(title = "Correlation across Spatial Variables") 

Correlation Matrix: Demographic Variables

In this section of the code, we compile a list of significant demographic variables, including factors such as racial composition, educational attainment, income levels, housing, and more. Upon analyzing the results, the generated correlation plot illustrates how these demographic factors interrelate.

In our analysis, we observe a nearly pure negative correlation between ‘percent white’ and ‘percent black.’ To mitigate multicollinearity, we recommend including only one of these variables in the final model. ‘Median income’ displays a robust and highly positive correlation with ‘sale price,’ making it a valuable addition to the model, given its strong predictive power. Similarly, ‘monthly cost,’ which represents the cost of utilities per month in houses with a mortgage, exhibits a noteworthy positive correlation with ‘sale price,’ making it another worthy inclusion in the final model, as it promises to enhance our predictive capabilities.

demvariables <- c(
  "sale_price", "PctWhite", "PctBlack", "PctHispanic", "PctBachelors", "PctPoverty", "med_income",
  
  "med_rent", "owner_units",         "renter_units" 
        , "same_house_75" 
       ,  "same_house_1" 
      ,   "monthly_costs_no_mortgage" 
      ,   "monthly_costs_with_mortgage" 
        , "med_gross_rent"     
        , "housing_units_with_heat" 
        , "edu_bachelors" 
       ,  "pop_below_poverty" 
       ,  "housing_units_high_speed_internet" 
       ,  "housing_units_no_vehicle"       
  )

numericdemVars <- data %>%
  st_drop_geometry(data) %>%
  select(demvariables)%>%
  na.omit()

ggcorrplot(
  round(cor(numericdemVars), 1), 
  p.mat = cor_pmat(numericdemVars),
  colors = c('#d7191c','#ffffbf','#2c7bb6'),
  type="lower",
  insig = "blank") +  
    labs(title = "Correlation across Demographic Variables") 

Final Variable Selection

Based on our initial correlation analyses, we went through an iterative process to determine the final set of metrics for our model. The code initially included some variables we considered, but to minimize errors like MAPE and MAE while maximizing accuracy, we made some substitutions. We also aimed to enhance the model’s generalizability by carefully choosing and rejecting specific variables. Finally, we settled on the variables presented. This comprehensive approach allowed us to tailor our model’s variable selection to not only reduce errors but also ensure the model’s applicability to a wide range of scenarios, ultimately contributing to more robust and dependable results in our regression analysis.

#variables <- c("sale_price", "number_of_bathrooms","dist_to_nearest_pvt_school", "dist_to_comm_corr", "dist_to_nearest_station", "dist_to_nearest_trail", "med_income", "dist_to_nearest_pool", "frontage", "central_air", "fireplaces", "BasementPresent", "exterior_condition", "PctWhite", "PctBlack", "total_livable_area", "BuildingAge",   "PctBachelors", "PctPoverty", "PricePerSqft","total_livable_area", "housing_units_with_heat", "PctHispanic", "housing_units_high_speed_nternet")

variables <- c ("sale_price" ,"total_livable_area","number_of_bathrooms", "fireplaces", "exterior_condition", "central_air", "PctWhite", "med_income", "monthly_costs_with_mortgage" , "PctPoverty", "dist_to_floodplain", "within_com_corr")

#dropping crime for now check later 

numericVars <- data %>%
  st_drop_geometry(data) %>%
  select(variables)%>%
  na.omit()

ggcorrplot(
  round(cor(numericVars), 1), 
  p.mat = cor_pmat(numericVars),
  colors = c('#d7191c','#ffffbf','#2c7bb6'),
  type="lower",
  insig = "blank") +  
    labs(title = "Correlation across numeric variables") 

OLS Regresssion Model

This code utilizes an OLS regression model to make accurate predictions about home prices. The advantage of an OLS model is its simplicity and ease of interpretation, making it a valuable tool for understanding linear relationships between variables.

Testing and Training Data

First we split the modelling data into training and testing sets, ensuring that the data is split randomly each time.

## Creating the training and test set
modelling_data <- data %>% filter(toPredict == "MODELLING")

set.seed(999) #makes sure data is split same way every time

## Splitting the data
split <- sample.split(modelling_data$objectid, SplitRatio = 0.75)

## Creating the training and test sets
train_data <- modelling_data[split,]
test_data <- modelling_data[!split,]

Regression Analysis

After running the regression analysis, we were able to come to some interesting relations between the sale price and different variables used in the analysis.

  1. The coefficients for ‘total_livable_area,’ ‘number_of_bathrooms,’ ‘fireplaces,’ ‘exterior_condition,’ ‘central_air,’ ‘PctWhite,’ ‘med_income,’ ‘monthly_costs_with_mortgage,’ and ‘within_com_corr’ are all statistically significant.

  2. Total Livable Area (total_livable_area): The coefficient of approximately 182.44 suggests that, for each additional unit of livable area, the ‘sale_price’ is expected to increase by around 182.44 units, provided that all other variables remain constant. This indicates a strong positive relationship between the size of the property and its sale price.

  3. Number of Bathrooms (number_of_bathrooms): With a coefficient of approximately 58659.04, each additional bathroom is associated with a substantial increase in ‘sale_price.’ This implies that properties with more bathrooms tend to command higher prices.

  4. Exterior Condition (exterior_condition): The coefficient of around -21749.80 signifies that, as the condition of the exterior deteriorates, ‘sale_price’ tends to decrease. This negative coefficient suggests that a well-maintained exterior has a positive impact on the property’s value.

  5. Monthly Costs with Mortgage (monthly_costs_with_mortgage): The coefficient of approximately 141.14 suggests that an increase in utility costs per month on houses with a mortgage leads to a higher ‘sale_price.’ This indicates that buyers may be willing to pay more for properties associated with higher monthly costs, possibly due to perceived value or amenities.

  6. Within Community Correlation (within_com_corr): With a coefficient of roughly 35629.83, a higher within-community correlation is associated with an increase in ‘sale_price.’ This implies that properties in areas where housing prices are positively correlated tend to command higher prices, reflecting the desirability of such neighborhoods.

We also then use the regression to predict home prices for the testing dataset, to then perform accuracy and generalizablity tests on it to refine the regression and clean and filter the dataset. We did this over and over, in order to choose the variables that made the most accurate and generalizable model.

#regression on training data

reg2 <- lm(sale_price ~ ., data = st_drop_geometry(train_data) %>%  
                                 dplyr::select(variables))

summary(reg2)
## 
## Call:
## lm(formula = sale_price ~ ., data = st_drop_geometry(train_data) %>% 
##     dplyr::select(variables))
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -925027  -53706    -880   47764 4303943 
## 
## Coefficients:
##                                  Estimate    Std. Error t value
## (Intercept)                 -225746.85879   10883.67836 -20.742
## total_livable_area              182.43776       2.64092  69.081
## number_of_bathrooms           58659.04249    3269.79875  17.940
## fireplaces                    37136.07889    4427.86873   8.387
## exterior_condition           -21749.80113    1520.52545 -14.304
## central_air                   27556.24176    2915.03121   9.453
## PctWhite                        615.15749      57.79495  10.644
## med_income                        0.46934       0.07668   6.120
## monthly_costs_with_mortgage     141.14049       3.47093  40.664
## PctPoverty                       39.37502     135.93348   0.290
## dist_to_floodplain               -2.33719       1.46705  -1.593
## within_com_corr               35629.83413    4668.07114   7.633
##                                         Pr(>|t|)    
## (Intercept)                 < 0.0000000000000002 ***
## total_livable_area          < 0.0000000000000002 ***
## number_of_bathrooms         < 0.0000000000000002 ***
## fireplaces                  < 0.0000000000000002 ***
## exterior_condition          < 0.0000000000000002 ***
## central_air                 < 0.0000000000000002 ***
## PctWhite                    < 0.0000000000000002 ***
## med_income                    0.0000000009595338 ***
## monthly_costs_with_mortgage < 0.0000000000000002 ***
## PctPoverty                                 0.772    
## dist_to_floodplain                         0.111    
## within_com_corr               0.0000000000000246 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 130100 on 13295 degrees of freedom
##   (223 observations deleted due to missingness)
## Multiple R-squared:  0.6867, Adjusted R-squared:  0.6864 
## F-statistic:  2649 on 11 and 13295 DF,  p-value: < 0.00000000000000022
## Plot regression 
#effect_plot(reg2, pred = number_of_bathrooms, interval = TRUE, plot.points = TRUE)
plot_summs(reg2, scale = TRUE)

#predicting price for testing data

test_data <-
  test_data %>%
  mutate(Price.Predict = predict(reg2, test_data),
         Price.Error = Price.Predict - sale_price,
         Price.AbsError = abs(Price.Predict - sale_price),
         Price.APE = (abs(Price.Predict - sale_price)) / Price.Predict)

Error Analysis: Accuracy

The mean absolute error (MAE) for our model is currently approximately $73,000, and the Mean Absolute Percentage Error (MAPE) stands at around 28%. We experimented with various variable combinations to reduce these metrics, and we managed to achieve results close to $20,000 (with a MAPE of 3%) by incorporating variables like “PricePerSqFT.” However, including these variables, which are derived from the provided sale_price, led to an overfit model that lacked generalizability – a crucial requirement for our application. Additionally, to predict home prices effectively, we must avoid using variables derived from home prices themselves, as this would create a convoluted and self-referential model.

# table of MAE and MAPE

## Accuracy - Mean Absolute Error
MAE <- data.frame(mean(test_data$Price.AbsError, na.rm = T))
MAPE <- data.frame(mean(test_data$Price.APE, na.rm = T)) #MAPE


reg.MAE.MAPE <- cbind(MAE, MAPE) %>%
  kable(format = "html", caption = "Regression MAE & MAPE (Figure 5.1)", col.names = c("Mean Absolute Error", "Mean Absolute Percentage Error")) %>%
  kable_styling("striped", full_width = FALSE)

reg.MAE.MAPE
Regression MAE & MAPE (Figure 5.1)
Mean Absolute Error Mean Absolute Percentage Error
72986.7 0.2861265

Generalizabiltiy

Using kfold we check generalizability

We used 100 folds for cross-validation. The high values of RMSE indicate that the model is being affected by outliers significantly. To address this issue, we conducted additional data cleaning and implemented outlier filtering techniques to minimize errors. However, the average RMSE value still remains above 100,000, making it relatively inaccurate for predicting home prices.

The R-squared statistic represents the proportion of the variance in the sale prices that the model can explain. It ranges from 0.3 to 0.9, with a mean value of 0.7. A higher R-squared value indicates a better fit of the model to the data. On average, our model can explain approximately 70% of the variance in the data, indicating that it possesses a reasonable level of generalizability.

test_data <-
  test_data %>%
  filter(sale_price < 10000000)

## K-Fold: Generalizability Cross-Validation

fitControl <- trainControl(method = "cv", number = 100)
set.seed(999)

reg.cv <- 
  train(sale_price ~ ., data = st_drop_geometry(test_data) %>% 
                                dplyr::select(variables),
method = "lm", trControl = fitControl, na.action = na.pass)

stargazer(as.data.frame(reg.cv$resample), type="text", digits=1, title="Cross Validation Results (5.2)", out = "CV.txt") #all cv
## 
## Cross Validation Results (5.2)
## ===================================================
## Statistic  N    Mean    St. Dev.   Min       Max   
## ---------------------------------------------------
## RMSE      100 113,131.9 66,213.0 61,449.3 583,198.8
## Rsquared  100    0.7      0.1      0.3       0.9   
## MAE       100 71,080.9  15,814.0 44,469.5 155,013.9
## ---------------------------------------------------

Most of our MAE seem to be within the 60,000 dollar to 80,000 dollar range (with a mean of 71,000) which is illustrated in the following histogram.

#plotting the cross validation stuff

ggplot(reg.cv$resample, aes(x=MAE)) +
  geom_histogram(fill = "#2c7bb6") +
  labs(title = "Cross Validation Tests in Mean Average Error", caption="Figure 5.3") +
  plotTheme()

The predicted and observed prices look evenly distributed above and below the perfect prediction line, below the $50,000 line. Our model moves below the perfect prediction line beyond 50,000 dollars indicating that our model may be under-predicting the values of high value homes.

test_data %>%
  dplyr::select(Price.Predict, sale_price) %>%
    ggplot(aes(sale_price, Price.Predict)) +
  geom_point() +
  stat_smooth(aes(sale_price, sale_price), 
             method = "lm", se = FALSE, size = 1, colour="#d7191c") + 
  stat_smooth(aes(Price.Predict, sale_price), 
              method = "lm", se = FALSE, size = 1, colour="#2c7bb6") +
  labs(title="Predicted Sale Price as a Function of Observed Price",
       subtitle="Red line represents a perfect prediction; Blue line represents prediction",
       caption = "Figure 6.2",
       x = "Observed Price",
       y = "Predicted Price") +
  plotTheme()+ xlim(0, 2000000) + ylim(0, 2000000)

Spatial Correlations

#use the same thng for wights and morans i 
#move the filters to this cel too 


# Spatial Lag for price
coords <- st_coordinates(test_data) 
neighborList <- knn2nb(knearneigh(coords, 5))
spatialWeights <- nb2listw(neighborList, style="W")
test_data$lagPrice <- lag.listw(spatialWeights, test_data$sale_price)

test_data$lagPriceError <- lag.listw(spatialWeights, test_data$Price.Error, NAOK = TRUE)

# Filtering greater than 0 values

test_data_filter <- test_data %>%
  filter(Price.Error > 0, lagPriceError > 0)

ggplot(data = test_data_filter, aes(lagPriceError, Price.Error)) +
  geom_point(size = .85,colour = "black") + 
  geom_smooth(method = "lm",colour = "red",size = 1.2) +
  labs(title="Price Errors",
       caption = "Figure 6.1") +
  plotTheme()

## Moran’s I

We observe a low Moran’s I value, indicating that errors are dispersed across our data. This suggests that there is no significant clustering of errors, a pattern that is also evident in the map. Consequently, it is probable that errors in our model may stem from other demographic or internal variables that we may not have accounted for. To improve the accuracy of our analysis, further investigation and inclusion of these potentially omitted variables might be necessary.

# for morans 1

# Spatial Lag for price
coords <- st_coordinates(test_data_filter) 
neighborList <- knn2nb(knearneigh(coords, 5))
spatialWeights <- nb2listw(neighborList, style="W")
test_data_filter$lagPrice <- lag.listw(spatialWeights, test_data_filter$sale_price)

test_data_filter$lagPriceError <- lag.listw(spatialWeights, test_data_filter$Price.Error, NAOK = TRUE)



moranTest <- moran.mc(na.omit(test_data_filter$Price.Error),
                      spatialWeights, nsim = 999)  

ggplot(as.data.frame(moranTest$res[c(1:999)]), aes(moranTest$res[c(1:999)])) +
  geom_histogram(binwidth = 0.005) +
  geom_vline(aes(xintercept = moranTest$statistic), colour = "#d7191c",size=1) +
  scale_x_continuous(limits = c(-0.5,0.5)) +
  labs(title="Observed and permuted Moran's I",
       subtitle= "Observed Moran's I in red",
       caption = "Figure 6.3",
       x="Moran's I",
       y="Count") +
  plotTheme()

ggplot() +
  geom_sf(data = tracts21, fill = "grey89", color = "grey89") +
  geom_sf(data = test_data, aes(colour = q5(Price.AbsError)), 
          show.legend = "point", size = .75) +
  scale_colour_manual(values = palettee,
                   labels=qBr(test_data,"Price.AbsError"),
                   name="Quintile\nBreaks") +
  labs(title="Map of Price Absolute Errors for Test Set", 
       subtitle= "Philadelphia",
      caption="Figure 6.4") +
  mapTheme()

ggplot() +
  geom_sf(data = tracts21, fill = "grey89", color = "grey89") +
  geom_sf(data = test_data, aes(colour = q5(Price.AbsError)), 
          show.legend = "point", size = .75) +
  scale_colour_manual(values = palettee,
                   labels=qBr(test_data,"Price.Predict"),
                   name="Quintile\nBreaks") +
  labs(title="Predicted Sale Price for Test Set", 
       subtitle = "Philadelphia",
      caption="Figure 6.4") +
  mapTheme()

# Differing Contexts

Here we try to observe trends of error and predictions within each census tract. We can see that the MAPE lies within a 0 to 1 range, and is higher in certain census tracts in North Philadelphia, indicating a potential missing spatial factor.

test_data <-
  test_data %>%
  group_by(census_tract) %>%
  mutate(MeanPrice = mean(sale_price))

test_data <-
  test_data %>%
  group_by(census_tract) %>%
  mutate(MAPE = mean(Price.APE)) %>%
  filter(MAPE> 0)

ggplot(test_data) +
  geom_point(aes(MeanPrice, MAPE)) +
  geom_smooth(method = "lm", aes(MeanPrice, MAPE), se = FALSE, colour = "green") +
  labs(title = "MAPE by Census Tract as a function of Mean Price by Census Tract",
       x = "Mean Home Price", y = "MAPE",
       caption = "Figure 7.5") +
  plotTheme()

## Tidycensus

Discussion

Assessing the model’s effectiveness is a nuanced task. While it exhibits moderate generalizability, the presence of notably high errors raises concerns about its predictive accuracy. One intriguing finding was the lack of spatial correlation with sale prices, defying the intuitive expectation that properties near landmarks or private schools would command higher prices. We were able to predict approximately 70% of the variance in sale prices, which is a reasonable level of explanatory power for a regression model.

Understanding the errors in predictions is complicated by their dispersed nature and the absence of clustering. The low Moran’s I statistic indicates a lack of spatial autocorrelation, making it challenging to attribute errors to specific geographic patterns. The influence of demographic factors, as outlined earlier, may be contributing to the observed errors.

When it comes to spatial variation, the dispersed errors make it difficult to identify specific areas where the model performed exceptionally well or poorly. However, it’s worth noting that there is a higher error count in South and Center City, suggesting the possibility of unaccounted spatial factors in those regions.

Prediction Challenge

All those things considered we used the model to predict home prices for the challenge data set. We notice that the number has gone down from 100 to 79, but we know that this is as a consequence of our data cleaning and removing data points with missing or incomplete values that could not be imputed (such as year built).

#for prediction 

reg1 <- lm(sale_price ~ ., data = st_drop_geometry(data_og) %>%  
             dplyr::filter(toPredict == "MODELLING") %>%
                                 dplyr::select(variables))
  
data_pred <-
  data_og %>%
  mutate(Price.Predict = predict(reg1,data_og))

final_pred <- st_drop_geometry(data_pred)%>%
   filter(toPredict== "CHALLENGE")%>%
  select(musaID,Price.Predict)%>%
  mutate(team = "Sam and Roshini")

write.csv(final_pred, "./outputs/skhare_rganesh_challenge.csv")

#map 

ggplot() +
  geom_sf(data = tracts21, fill = "grey89", color = "grey89") +
  geom_sf(data = data_pred, aes(colour = q5(Price.Predict)), 
          show.legend = "point", size = .75) +
  scale_colour_manual(values = palettee,
                   labels=qBr(data_pred,"Price.Predict"),
                   name="Quintile\nBreaks") +
  labs(title="Predicted Sale Price for all Data", 
       subtitle = "Philadelphia") +
  mapTheme()

Conclusion

We would recommend this model to Zillow if the errors weren’t so high. The model performs well in many contexts but if time was no bound, we would have really liked to explore more variables and refine it more to minimize the errors before sharing the model.

LS0tDQp0aXRsZTogIlByZWRpY3RpbmcgSG91c2luZyBQcmljZXMgaW4gUGhpbGFkZWxwaGlhLCBQQSINCmF1dGhvcjogIlNhbXJpZGRoaSBLaGFyZSwgUm9zaGluaSBHYW5lc2giDQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRoZW1lOiBqb3VybmFsICANCi0tLQ0KDQojIEludHJvZHVjdGlvbg0KDQpUaGUgUGhpbGFkZWxwaGlhIGhvdXNpbmcgbWFya2V0IGlzIGEgZHluYW1pYyBhbmQgZGl2ZXJzZSBsYW5kc2NhcGUgaW5mbHVlbmNlZCBieSB2YXJpb3VzIGZhY3RvcnMgdGhhdCBpbXBhY3QgYm90aCBwcmljZXMgYW5kIG92ZXJhbGwgbWFya2V0IGNvbmRpdGlvbnMuIFRoaXMgbWFya2V0IHJlZmxlY3RzIHRoZSBlY29ub21pYyBhbmQgc29jaWFsIGR5bmFtaWNzIG9mIHRoZSBjaXR5IGFuZCB0aGUgc3Vycm91bmRpbmcgcmVnaW9uLkl0IGVuY29tcGFzc2VzIGEgd2lkZSByYW5nZSBvZiBuZWlnaGJvcmhvb2RzLCBlYWNoIHdpdGggaXRzIHVuaXF1ZSBjaGFyYWN0ZXIsIGFtZW5pdGllcywgYW5kIGhvdXNpbmcgc3R5bGVzLiBUaGUgY2l0eSdzIGRpdmVyc2UgaG91c2luZyBzdG9jayBpbmNsdWRlcyByb3dob3VzZXMsIHRvd25ob21lcywgYXBhcnRtZW50cywgYW5kIHNpbmdsZS1mYW1pbHkgaG9tZXMsIGNhdGVyaW5nIHRvIGEgdmFyaWV0eSBvZiBsaWZlc3R5bGVzIGFuZCBwcmVmZXJlbmNlcy4NCg0KPGltZyBzcmM9Ii4vaW1hZ2VzLzMzcmQtU3RyZWV0LUNvcm5lci1vZi1XZXN0LUxlaGlnaC1BdmUuanBnIiB3aWR0aD0iMzAwIiBhbGlnbj0icmlnaHQiIHN0eWxlPSJkaXNwbGF5OiBpbmxpbmU7IG1hcmdpbjogMCAxMHB4OyIvPg0KDQpTaWduaWZpY2FuY2Ugb2YgcHJvamVjdA0KDQpIb3VzaW5nIHByaWNlcyBpbiBQaGlsYWRlbHBoaWEgaGF2ZSBleHBlcmllbmNlZCBmbHVjdHVhdGlvbnMgb3ZlciB0aGUgeWVhcnMsIGluZmx1ZW5jZWQgYnkgYSBjb21iaW5hdGlvbiBvZiBsb2NhbCBhbmQgbmF0aW9uYWwgZmFjdG9ycy4gSG93ZXZlciwgaXQncyBpbXBvcnRhbnQgdG8gbm90ZSB0aGF0IHRoZXNlIHByaWNlcyBjYW4gdmFyeSBzaWduaWZpY2FudGx5IGJhc2VkIG9uIGZhY3RvcnMgc3VjaCBhcyB0aGUgbmVpZ2hib3Job29kLCBwcm9wZXJ0eSB0eXBlLCBhbmQgY29uZGl0aW9uIG9mIHRoZSBob21lLiANCg0KU2FsZSBwcmVkaWN0aW9uIGFsZ29yaXRobXMgYXJlIHBvd2VyZnVsIHRvb2xzIHRoYXQgb2ZmZXIgYmVuZWZpdHMgdG8gYSB3aWRlIHJhbmdlIG9mIHN0YWtlaG9sZGVycyBpbiB0aGUgaG91c2luZyBtYXJrZXQuIFRoZXkgcHJvbW90ZSBpbmZvcm1lZCBkZWNpc2lvbi1tYWtpbmcsIHVuY292ZXIgaW5lcXVhbGl0aWVzLCBhbmQgY29udHJpYnV0ZSB0byB0aGUgb3ZlcmFsbCBoZWFsdGggYW5kIGZhaXJuZXNzIG9mIHRoZSByZWFsIGVzdGF0ZSBtYXJrZXQuDQoNClRoaXMgcHJvamVjdCBidWlsZHMgb24gdGhpcyB0aGVvcnkgYW5kIGFpbXMgdG8gZXZhbHVhdGUgdGhlIHZhcmlvdXMgZmFjdG9ycyBhZmZlY3RpbmcgaG91c2luZyBwcmljZXMgdG8gY3JlYXRlIGFuIGFjY3VyYXRlIGFuZCBnZW5lcmFsaXphYmxlIE9MUyByZWdyZXNzaW9uIG1vZGVsIHRvIHByZWRpY3QgdGhlIGhvdXNpbmcgcHJpY2VzIGluIFBoaWxhZGVscGhpYS4NCg0KS2V5IGNoYWxsZW5nZXMNCg0KQnVpbGRpbmcgYSBwcmVkaWN0aXZlIG1vZGVsIGZvciBwcm9wZXJ0eSB2YWx1YXRpb24gaXMgZGlmZmljdWx0IGdpdmVuIHRoZSBpbnRyaWNhY2llcyBpbnZvbHZlZCBpbiBkYXRhIHByb2Nlc3NpbmcgYW5kIGludGVycHJldGF0aW9uLiAgSXQgaW52b2x2ZXMgc29ydGluZyB0aHJvdWdoIGxvdHMgb2YgZGF0YSwgY2hvb3NpbmcgdGhlIHJpZ2h0IGZlYXR1cmVzLCB1bmRlcnN0YW5kaW5nIHRoZSBudWFuY2VkIGluZmx1ZW5jZSBvZiBuZWlnaGJvcmhvb2QgY2hhcmFjdGVyaXN0aWNzLCBhZGRyZXNzaW5nIGNvbGxpbmVhcml0eSBhbW9uZyB2YXJpYWJsZXMsIHNlbGVjdGluZyBhcHByb3ByaWF0ZSBtb2RlbGluZyB0ZWNobmlxdWVzLCBldmFsdWF0aW5nIG1vZGVsIHBlcmZvcm1hbmNlLCBlbnN1cmluZyBpbnRlcnByZXRhYmlsaXR5LCBhbmQgYnVpbGRpbmcgYSBnZW5lcmFsaXphYmxlIG1vZGVsLiANCg0KIyMgUHJvY2Vzcw0KDQpUaGUgcHJvY2VzcyBvZiBjcmVhdGluZyB0aGlzIG1vZGVsIHdhcyBleHRyZW1lbHkgaXRlcmF0aXZlLCBhbmQgaW52b2x2ZWQgbXVsdGlwbGUgcm91bmRzIG9mIHRyaWFsIGFuZCBlcnJvciwgcGFydGljdWxhcmx5IGluIGF0dGVtcHRzIHRvIG1pbmltaXplIHRoZSBlcnJvcnMgaW4gcHJlZGljdGlvbnMuIFRoZSBtYXBzLCBjb3JyZWxhdGlvbiBtYXRyaXggYW5kIHNjYXR0ZXJwbG90cyBhcmUgYWxsIGlsbHVzdHJhdGl2ZSBvZiB2YXJpYWJsZXMgdGhhdCB3ZXJlIGNvbnNpZGVyZWQsIHdyYW5nbGVkIGFuZCBmaWx0ZXJlZCBhcyBwYXJ0IG9mIHRoZSBkYXRhIGFuYWx5c2lzIHByb2Nlc3MuICANCg0KIyMgU3VtbWFyeSBvZiBSZXN1bHRzDQoNClRoZSBtb2RlbCBleGhpYml0cyBtb2RlcmF0ZSBnZW5lcmFsaXphYmlsaXR5LCBob3dldmVyLCB0aGUgcHJlc2VuY2Ugb2Ygbm90YWJseSBoaWdoIGVycm9ycyByYWlzZXMgY29uY2VybnMgYWJvdXQgaXRzIHByZWRpY3RpdmUgYWNjdXJhY3kuIEluIG91ciBwcm9jZXNzIG9mIGV4cGVyaW1lbnRhdGlvbiwgd2Ugd2VyZSBhYmxlIHRvIHByZWRpY3QgYXBwcm94aW1hdGVseSA3MCUgb2YgdGhlIHZhcmlhbmNlIGluIHNhbGUgcHJpY2VzLCB3aGljaCBpcyBhIHJlYXNvbmFibGUgbGV2ZWwgb2YgZXhwbGFuYXRvcnkgcG93ZXIgZm9yIGEgcmVncmVzc2lvbiBtb2RlbC4gVGhlIGVycm9ycyBhY3Jvc3MgdGhlIG1vZGVsIHNlZW0gdG8gYmUgZGlzcGVyc2VkIGFuZCBkbyBub3QgZGlzcGxheSBhbnkgc2lnbmlmaWNhbnQgc3BhdGlhbCBhdXRvLWNvcnJlbGF0aW9uLiANCg0KVGhpcyBjb2RlIGlzIGJ1aWx0IHVwb24gdGhlIGNsYXNzd29yayBkaXNjdXNzZWQgW2hlcmVdKGh0dHBzOi8vZ2l0aHViLmNvbS9tYWZpY2htYW4vbXVzYV81MDgwXzIwMjMvdHJlZS9tYWluKS4NCg0KYGBge3IgU2V0dXAgS25pdHRpbmcgUGFyYW1ldGVycywgaW5jbHVkZT1GQUxTRX0NCiAga25pdHI6Om9wdHNfY2h1bmskc2V0KA0KICAgIGVjaG8gPSBUUlVFLA0KICAgIHdhcm5pbmcgPSBGQUxTRSwNCiAgICBtZXNzYWdlID0gRkFMU0UsDQogICBvdXQud2lkdGggPSAnMTAwJScsDQogICAgZmlnLnJldGluYSA9Mw0KICApDQpgYGANCg0KIyMgUiBTZXR1cCBhbmQgSW5zdGFsbGluZyBQYWNrYWdlcw0KDQpUaGlzIGNvZGUgY2h1bmsgaGFuZGxlcyB0aGUgZXNzZW50aWFsIHRhc2tzIG9mIGxvYWRpbmcgbmVjZXNzYXJ5IHBhY2thZ2VzLCBjb25maWd1cmluZyB0aGUgQ2Vuc3VzIEFQSSBrZXksIGRlZmluaW5nIGNsYXNzIGZ1bmN0aW9ucywgc3BlY2lmeWluZyBhIGNvbG9yIHBhbGV0dGUsIGFuZCBtYW5hZ2luZyBnbG9iYWwgZW52aXJvbm1lbnQgc2V0dGluZ3MuDQoNCmBgYHtyIFNldCB1cCBQYWNrYWdlcywgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9DQoNCiMgTG9hZGluZyBsaWJyYXJpZXMNCg0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHRpZHljZW5zdXMpDQpsaWJyYXJ5KHNmKQ0KbGlicmFyeShrYWJsZUV4dHJhKQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkodmlyaWRpcykNCmxpYnJhcnkoc3RyaW5ncikNCmxpYnJhcnkodGlncmlzKQ0KbGlicmFyeShnZ2NvcnJwbG90KQ0KbGlicmFyeShzdGFyZ2F6ZXIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShjYVRvb2xzKQ0KbGlicmFyeShzcGRlcCkNCmxpYnJhcnkoY2FyZXQpDQpsaWJyYXJ5KGp0b29scykgICAgIA0KbGlicmFyeShnZ3N0YW5jZSkgDQpsaWJyYXJ5KGdncHVicikgICAgDQpsaWJyYXJ5KGJyb29tLm1peGVkKSANCg0KIyBTZXR0aW5nIHBhcmFtZXRlcnMgZm9yIHNjaWVudGlmaWMgbm90YXRpb24NCg0Kb3B0aW9ucyhzY2lwZW49OTk5KQ0Kb3B0aW9ucyh0aWdyaXNfY2xhc3MgPSAic2YiKQ0KDQojIEZ1bmN0aW9ucyBhbmQgZGF0YSBkaXJlY3RvcnkNCg0Kc291cmNlKCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vdXJiYW5TcGF0aWFsL1B1YmxpYy1Qb2xpY3ktQW5hbHl0aWNzLUxhbmRpbmcvbWFzdGVyL2Z1bmN0aW9ucy5yIikNCg0KIyBJbnZva2luZyBjb2xvciBwYWxldHRlcyB0byBiZSB1c2VkDQoNCnBhbGV0dGVlIDwtIGMoJyNkNzE5MWMnLCcjZmRhZTYxJywnI2ZmZmZiZicsJyNhYmQ5ZTknLCcjMmM3YmI2JykNCg0KIyBSZWdpc3RlcmluZyBBUEkgS2V5IHRvIGJlIHVzZWQNCg0KY2Vuc3VzX2FwaV9rZXkoJ2JmMmQ1MDc2NTFiNWE2MjFkYmFkZDQ0NTMzZmI0ZjNkZWFhYjI2YmYnLCBvdmVyd3JpdGUgPSBUUlVFKQ0KDQpgYGANCg0KIyBEYXRhIFdyYW5nbGluZw0KDQpDZW5zdXMsIE9wZW5EYXRhIFBoaWxseSBhbmQgcHJvdmlkZWQgRGF0YXNldCANCg0KIyMgUHJvdmlkZWQgRGF0YXNldCBhbmQgSW50ZXJuYWwgVmFyaWFibGVzIA0KDQpUaGUgY29kZSBiZWxvdyBpcyB1c2VkIHRvIGltcG9ydCB0aGUgZm91bmRhdGlvbmFsIGRhdGFzZXQgZm9yIG91ciBtb2RlbC4gVGhpcyBkYXRhc2V0IGNvbnRhaW5zIGluZm9ybWF0aW9uIG9uIGhvbWUgc2FsZXMgcHJpY2VzIGFuZCBwcm9wZXJ0eSBjaGFyYWN0ZXJpc3RpY3MgaW4gUGhpbGFkZWxwaGlhLCBQZW5uc3lsdmFuaWEgZm9yIHRoZSB5ZWFycyAyMDIyIGFuZCAyMDIzLiBUaGUgbW9kZWwgd2UgYXJlIGRldmVsb3BpbmcgaXMgZGVzaWduZWQgdG8gbWFrZSBwcmVkaWN0aW9ucyBhYm91dCBob21lIHByaWNlcyBhbmQgdXRpbGl6ZXMgc3BlY2lmaWMgcHJvcGVydHkgYXR0cmlidXRlcyBmcm9tIHRoaXMgZGF0YXNldCB0byBlbmhhbmNlIHRoZSBhY2N1cmFjeSBvZiB0aG9zZSBwcmVkaWN0aW9ucy4NCg0KVGhlIGNvZGUgZGVtb25zdHJhdGVzIGRhdGEgbWFuaXB1bGF0aW9uIHVzaW5nIHRoZSBkcGx5ciBwYWNrYWdlIHRvIHJlZmluZSB0aGUgZGF0YXNldCBieSBzZWxlY3Rpbmcgc3BlY2lmaWMgY29sdW1ucyBhbmQgZmlsdGVyaW5nIHJvd3MgYmFzZWQgb24gY3JpdGVyaWEuIFRoZXNlIG9wZXJhdGlvbnMgYXJlIGNhcnJpZWQgb3V0IGJlY2F1c2UgdGhlIHJhdyBkYXRhc2V0IGlzIHVudGlkeSBhbmQgaW5jb21wbGV0ZS4gVGhlIG9yaWdpbmFsIGRhdGFzZXQgaXMgZnVydGhlciBtb2RpZmllZCBieSBmaWx0ZXJpbmcgcm93cyBzcGVjaWZpYyB0byBQaGlsYWRlbHBoaWEgQ2l0eSBhbmQgYnkgZXhjbHVkaW5nIGNlcnRhaW4gY29sdW1ucyB3aXRoIGEgbWFqb3JpdHkgb2YgbWlzc2luZyB2YWx1ZXMuDQoNCmBgYHtyIFJlYWRpbmcgRGF0YSwgcmVzdWx0cz0gJ2hpZGUnfQ0KDQojIFJlYWRpbmcgRGF0YQ0KDQpkYXRhIDwtIA0KICBzdF9yZWFkKCIuL2RhdGEvc3R1ZGVudERhdGEuZ2VvanNvbiIpICU+JQ0KICBzdF90cmFuc2Zvcm0oJ0VTUkk6MTAyMjg2JykNCg0KIyBEcm9wcGluZyBjb2x1bW5zIHdpdGggbm8gdmFsdWVzIGFuZCBmaWx0ZXJpbmcgdmFsdWVzIHdpdGhpbiBQaGlsYWRlbHBoaWENCg0KZGF0YSA8LSAgZGF0YSAlPiUgDQogIHNlbGVjdCgtY3Jvc3NfcmVmZXJlbmNlLCAtZGF0ZV9leHRlcmlvcl9jb25kaXRpb24sIC1tYWlsaW5nX2FkZHJlc3NfMiwgLW1haWxpbmdfY2FyZV9vZiwgLXVuZmluaXNoZWQsIC11dGlsaXR5LCAtY2F0ZWdvcnlfY29kZSwgLWNhdGVnb3J5X2NvZGVfZGVzY3JpcHRpb24sIC1leGVtcHRfbGFuZCwgLXNlcGFyYXRlX3V0aWxpdGllcywgLXNld2VyLCAtIHNpdGVfdHlwZSwgLWhvdXNlX2V4dGVuc2lvbiwgLXN0cmVldF9kaXJlY3Rpb24sIC1zdWZmaXgsIC1nYXJhZ2VfdHlwZSwgLWdlbmVyYWxfY29uc3RydWN0aW9uICklPiUgDQogIGZpbHRlcihtYWlsaW5nX2NpdHlfc3RhdGUgPT0gIlBISUxBREVMUEhJQSBQQSIgKQ0KDQpgYGANCiMjIyBDYXRlZ29yaWNhbCBWYXJpYWJsZXMgDQoNClRoZSBuZXh0IHN0ZXAgaXMgcHJlZG9taW5hbnRseSBmb2N1c2VkIG9uIGRhdGEgY2xlYW5pbmcgYW5kIHRyYW5zZm9ybWF0aW9uIGJhc2VkIG9uIHRoZSBpbmZvcm1hdGlvbiBnaXZlbiBpbiB0aGUgbWV0YWRhdGEgZm9yIHRoZSBkYXRhc2V0IGJ5IHRoZSBDaXR5IG9mIFBoaWxhZGVscGhpYS4gT3ZlcmFsbCwgdGhpcyBjb2RlIGFpbXMgdG8gY2xlYW4gYW5kIGNhdGVnb3JpemUgdmFyaW91cyBjb2x1bW5zIGluIHRoZSBkYXRhc2V0LiBJdCBkb2VzIHRoaXMgYnkgaGFuZGxpbmcgbWlzc2luZyB2YWx1ZXMgYXMgd2VsbCBhcyBhc3NpZ25pbmcgY2F0ZWdvcmllcyBhbmQgYmluYXJ5IHZhbHVlcyBmb3IgYmV0dGVyIGFuYWx5c2lzIGFuZCBtb2RlbGluZy4NCg0KDQpgYGB7ciBDbGVhbmluZyBEYXRhIGJhc2VkIG9uIE1ldGFkYXRhLCByZXN1bHRzPSAnaGlkZSd9DQoNCiMjIEZpbHRlcmluZyBvdXQgOSByb3dzIHdoZXJlIHllYXIgYnVpbHQgaXMgbm90IGdpdmVuDQoNCmRhdGEgPC0gIGRhdGEgJT4lIA0KICBmaWx0ZXIoeWVhcl9idWlsdCA+IDAgKQ0KDQojIyBDYXRlZ29yaXppbmcgaWYgYSBCYXNlbWVudCBpcyBwcmVzZW50DQoNCmRhdGEgPC0gZGF0YSAlPiUNCiAgbXV0YXRlKEJhc2VtZW50UHJlc2VudCA9IGNhc2Vfd2hlbihiYXNlbWVudHMgPT0gJ0EnIHwNCiAgYmFzZW1lbnRzID09ICdCJyB8DQogIGJhc2VtZW50cyA9PSAnQycgfA0KICBiYXNlbWVudHMgPT0gJ0QnIHwNCiAgYmFzZW1lbnRzID09ICdFJyB8DQogIGJhc2VtZW50cyA9PSAnRicgfA0KICBiYXNlbWVudHMgPT0gJ0cnIHwNCiAgYmFzZW1lbnRzID09ICdIJyB8DQogIGJhc2VtZW50cyA9PSAnSScgfA0KICBiYXNlbWVudHMgPT0gJ0onIH4gMSwNCiAgYmFzZW1lbnRzID09ICcwJyB+IDApKQ0KDQojIEFzc2lnbmluZyB2YWx1ZSBvZiAwIHRvICdOQScgcm93cyBiYXNlZCBvbiBkZXNjcmlwdGlvbiBpbiBtZXRhZGF0YQ0KDQpkYXRhJEJhc2VtZW50UHJlc2VudFtpcy5uYShkYXRhJEJhc2VtZW50UHJlc2VudCldIDwtIDANCg0KIyMgQ2F0ZWdvcml6aW5nIEJhc2VtZW50IFR5cGUNCg0KbGlicmFyeShkcGx5cikNCg0KZGF0YSA8LSBkYXRhICU+JQ0KICBtdXRhdGUoQmFzZW1lbnRUeXBlID0gY2FzZV93aGVuKGJhc2VtZW50cyA9PSAnQScgfiAnRnVsbCBzaXplIGZpbmlzaGVkJywNCiAgYmFzZW1lbnRzID09ICdCJyB+ICdGdWxsIHNpemUgc2VtaS1maW5pc2hlZCcsDQogIGJhc2VtZW50cyA9PSAnQycgfiAnRnVsbCBzaXplIHVuZmluaXNoZWQnLA0KICBiYXNlbWVudHMgPT0gJ0QnIH4gJ0Z1bGwgc2l6ZSB1bmtub3duIGZpbmlzaCcsDQogIGJhc2VtZW50cyA9PSAnRScgfiAnUGFydGlhbCBzaXplIGZpbmlzaGVkJywNCiAgYmFzZW1lbnRzID09ICdGJyB+ICdQYXJ0aWFsIHNpemUgc2VtaS1maW5pc2hlZCcsDQogIGJhc2VtZW50cyA9PSAnRycgfiAnUGFydGlhbCBzaXplIHVuZmluaXNoZWQnLA0KICBiYXNlbWVudHMgPT0gJ0gnIH4gJ1BhcnRpYWwgc2l6ZSB1bmtub3duIGZpbmlzaCcsDQogIGJhc2VtZW50cyA9PSAnSScgfiAnVW5rbm93biBzaXplIGZpbmlzaGVkJywNCiAgYmFzZW1lbnRzID09ICdKJyB+ICdVbmtub3duIHNpemUgdW5maW5pc2hlZCcsDQogIGJhc2VtZW50cyA9PSAnMCcgfiAnTm8gYmFzZW1lbnQnKSkNCg0KIyBBc3NpZ25pbmcgdmFsdWUgb2YgMCB0byAnTkEnIHJvd3MgYmFzZWQgb24gZGVzY3JpcHRpb24gaW4gbWV0YWRhdGENCg0KZGF0YSRCYXNlbWVudFR5cGVbaXMubmEoZGF0YSRCYXNlbWVudFR5cGUpXSA8LSAiTm8gYmFzZW1lbnQiDQpkYXRhJGJhc2VtZW50c1tpcy5uYShkYXRhJGJhc2VtZW50cyldIDwtIDANCg0KIyMgQ2F0ZWdvcml6aW5nIGJhc2VkIG9uIENlbnRyYWwgQWlyDQoNCmRhdGEkY2VudHJhbF9haXIgPC0gaWZlbHNlKGRhdGEkY2VudHJhbF9haXIgPT0gJ1knLCAxLCAwKQ0KDQojIyBDYXRlZ29yaXppbmcgYmFzZWQgb24gRXh0ZXJpb3IgQ29uZGl0aW9uDQoNCmRhdGEgPC0gZGF0YSAlPiUNCiAgbXV0YXRlKEV4dGVyaW9yQ29uZGl0aW9uVHlwZSA9IGNhc2Vfd2hlbihleHRlcmlvcl9jb25kaXRpb24gPT0gJzEnIHwNCiAgZXh0ZXJpb3JfY29uZGl0aW9uID09ICcyJyB8DQogIGV4dGVyaW9yX2NvbmRpdGlvbiA9PSAnMycgfiAnR29vZCBDb25kaXRpb24nLA0KICBleHRlcmlvcl9jb25kaXRpb24gPT0gJzQnIHwNCiAgZXh0ZXJpb3JfY29uZGl0aW9uID09ICc1JyB+ICdBdmVyYWdlIENvbmRpdGlvbicsDQogIGV4dGVyaW9yX2NvbmRpdGlvbiA9PSAnNicgfiAnQmVsb3cgQXZlcmFnZSBDb25kaXRpb24nLA0KICBleHRlcmlvcl9jb25kaXRpb24gPT0gJzcnIH4gJ1ZhY2FudCBhbmQgU2VhbGVkJykpDQoNCiMgQXNzaWduaW5nIHZhbHVlIHRvIHNpbmdsZSAnTkEnIHJvdyBiYXNlZCBvbiAnaW50ZXJpb3JfY29uc3RydWN0aW9uJyByYXRpbmcgZm9yIHNhbWUgcm93DQoNCmRhdGEkRXh0ZXJpb3JDb25kaXRpb25UeXBlW2lzLm5hKGRhdGEkRXh0ZXJpb3JDb25kaXRpb25UeXBlKV0gPC0gIkdvb2QgQ29uZGl0aW9uIg0KZGF0YSRleHRlcmlvcl9jb25kaXRpb25baXMubmEoZGF0YSRleHRlcmlvcl9jb25kaXRpb24pXSA8LSAzDQoNCiMjIENhdGVnb3JpemluZyBiYXNlZCBvbiBGaXJlcGxhY2VzDQoNCiMgQXNzaWduaW5nIHZhbHVlIG9mIDAgdG8gJ05BJyByb3dzIGJhc2VkIG9uIGRlc2NyaXB0aW9uIGluIG1ldGFkYXRhDQoNCmRhdGEkZmlyZXBsYWNlc1tpcy5uYShkYXRhJGZpcmVwbGFjZXMpXSA8LSAwDQoNCiMjIENhdGVnb3JpemluZyBiYXNlZCBvbiBGdWVsIFNvdXJjZXMNCg0KZGF0YSA8LSBkYXRhICU+JQ0KICBtdXRhdGUoRnVlbFR5cGUgPSBjYXNlX3doZW4oZnVlbCA9PSAnQScgfiAnTmF0dXJhbCBHYXMgUG93ZXJlZCcsDQogIGZ1ZWwgPT0gJ0InIH4gJ09pbCBQb3dlcmVkJywNCiAgZnVlbCA9PSAnQycgfiAnRWxlY3RyaWMgUG93ZXJlZCcsDQogIGZ1ZWwgPT0gJ0UnIH4gJ1NvbGFyIFBvd2VyZWQnLA0KICBmdWVsID09ICdHJyB+ICdGdWVsIFNvdXJjZSBVbmtub3duJykpDQoNCiMgQXNzaWduaW5nIHZhbHVlIG9mIFVua25vd24vRyB0byAnTkEnIHJvd3MgYmFzZWQgb24gZGVzY3JpcHRpb24gaW4gbWV0YWRhdGENCg0KZGF0YSRGdWVsVHlwZVtpcy5uYShkYXRhJEZ1ZWxUeXBlKV0gPC0gIkZ1ZWwgU291cmNlIFVua25vd24iDQpkYXRhJGZ1ZWxbaXMubmEoZGF0YSRmdWVsKV0gPC0gIkciDQoNCiMjIENhdGVnb3JpemluZyBiYXNlZCBvbiBHYXJhZ2UgUHJlc2VuY2UNCg0KZGF0YSA8LSBkYXRhICU+JQ0KICBtdXRhdGUoR2FyYWdlUHJlc2VudCA9IGNhc2Vfd2hlbihnYXJhZ2Vfc3BhY2VzID09ICcwJyB+IDAsDQogIGdhcmFnZV9zcGFjZXMgPT0gJzEnIHwNCiAgZ2FyYWdlX3NwYWNlcyA9PSAnMicgfA0KICBnYXJhZ2Vfc3BhY2VzID09ICczJyB+IDEpKQ0KDQojIEFzc2lnbmluZyB2YWx1ZSBvZiAwIHRvICdOQScgcm93cyBiYXNlZCBvbiBkZXNjcmlwdGlvbiBpbiBtZXRhZGF0YQ0KDQpkYXRhJEdhcmFnZVByZXNlbnRbaXMubmEoZGF0YSRHYXJhZ2VQcmVzZW50KV0gPC0gMA0KZGF0YSRnYXJhZ2Vfc3BhY2VzW2lzLm5hKGRhdGEkZ2FyYWdlX3NwYWNlcyldIDwtIDANCg0KIyMgQ2F0ZWdvcml6aW5nIGJhc2VkIG9uIEdhcmFnZSBUeXBlcw0KDQpkYXRhIDwtIGRhdGEgJT4lDQogIG11dGF0ZShHYXJhZ2VUeXBlID0gY2FzZV93aGVuKGdhcmFnZV9zcGFjZXMgPT0gJzAnIH4gJ05vIEdhcmFnZScsDQogIGdhcmFnZV9zcGFjZXMgPT0gJzEnIH4gJ1NpbmdsZSBHYXJhZ2UnLA0KICBnYXJhZ2Vfc3BhY2VzID09ICcyJyB8DQogIGdhcmFnZV9zcGFjZXMgPT0gJzMnIH4gJ011bHRpcGxlIEdhcmFnZXMnKSkNCg0KIyBBc3NpZ25pbmcgdmFsdWUgb2YgMCB0byAnTkEnIHJvd3MgYmFzZWQgb24gZGVzY3JpcHRpb24gaW4gbWV0YWRhdGENCg0KZGF0YSRHYXJhZ2VUeXBlW2lzLm5hKGRhdGEkR2FyYWdlVHlwZSldIDwtICJObyBHYXJhZ2UiDQpkYXRhJGdhcmFnZV9zcGFjZXNbaXMubmEoZGF0YSRnYXJhZ2Vfc3BhY2VzKV0gPC0gMA0KDQoNCmRhdGEgPC0gZGF0YSAlPiUNCiAgbXV0YXRlKEludGVyaW9yQ29uZGl0aW9uVHlwZSA9IGNhc2Vfd2hlbiggaW50ZXJpb3JfY29uZGl0aW9uID09ICcwJyB8DQogIGludGVyaW9yX2NvbmRpdGlvbiA9PSAnMScgfA0KICBpbnRlcmlvcl9jb25kaXRpb24gPT0gJzInIHwNCiAgaW50ZXJpb3JfY29uZGl0aW9uID09ICczJyB+ICdHb29kIENvbmRpdGlvbicsDQogIGludGVyaW9yX2NvbmRpdGlvbiA9PSAnNCcgfiAnQXZlcmFnZSBDb25kaXRpb24nLA0KICBpbnRlcmlvcl9jb25kaXRpb24gPT0gJzUnIH4gJ0JlbG93IEF2ZXJhZ2UgQ29uZGl0aW9uJywNCiAgaW50ZXJpb3JfY29uZGl0aW9uID09ICc2JyB8DQogIGludGVyaW9yX2NvbmRpdGlvbiA9PSAnNycgfiAnVmFjYW50IGFuZC9vciBTZWFsZWQnKSkNCg0KIyBBc3NpZ25pbmcgdmFsdWVzIHRvIDAgb3IgJ05BJyByb3dzIGJhc2VkIG9uIGV4dGVyaW9yIGNvbmRpdGlvbiBzaW5jZSBpbnRlcmlvciBhbmQgZXh0ZXJpb3IgY29uZGl0aW9ucyBhcmUgZXF1YWwgaW4gYWxtb3N0IGFsbCBjYXNlcw0KDQpkYXRhJEludGVyaW9yQ29uZGl0aW9uVHlwZVtpcy5uYShkYXRhJEludGVyaW9yQ29uZGl0aW9uVHlwZSldIDwtIGMoZGF0YSRFeHRlcmlvckNvbmRpdGlvblR5cGUpDQoNCiMjIENhdGVnb3JpemluZyBiYXNlZCBvbiBWaWV3IFR5cGUNCg0KZGF0YSA8LSBkYXRhICU+JQ0KICBtdXRhdGUoVmlld1R5cGUgPSBjYXNlX3doZW4odmlld190eXBlID09ICcwJyB+ICdOYXR1cmUgb2YgVmlldyBVbmtub3duJywNCiAgdmlld190eXBlID09ICdJJyB+ICdUeXBpY2FsIFZpZXcnLA0KICB2aWV3X3R5cGUgPT0gJ0EnIH4gJ1NreWxpbmUgVmlldycsDQogIHZpZXdfdHlwZSA9PSAnQicgfiAnUml2ZXIgVmlldycsDQogIHZpZXdfdHlwZSA9PSAnQycgfiAnUGFyayBWaWV3JywNCiAgdmlld190eXBlID09ICdEJyB+ICdDb21tZXJjaWFsIEFyZWEgVmlldycsDQogIHZpZXdfdHlwZSA9PSAnRScgfiAnSW5kdXN0cmlhbCBBcmVhIFZpZXcnLA0KICB2aWV3X3R5cGUgPT0gJ0gnIH4gJ1ZpZXcgb2YgTGFuZG1hcmsnKSkNCg0KIyBBc3NpZ25pbmcgdmFsdWUgdG8gTkEgcm93cyB0byBuYXR1cmUgb2YgdmlldyB1bmtub3duDQoNCmRhdGEkVmlld1R5cGVbaXMubmEoZGF0YSRWaWV3VHlwZSldIDwtICJOYXR1cmUgb2YgVmlldyBVbmtub3duIg0KZGF0YSR2aWV3X3R5cGVbaXMubmEoZGF0YSR2aWV3X3R5cGUpXSA8LSAwDQoNCiMjIENhdGVnb3JpemluZyBiYXNlZCBvbiBUb3BvZ3JhcGh5IFR5cGUNCg0KZGF0YSA8LSBkYXRhICU+JQ0KICBtdXRhdGUoVG9wb2dyYXBoeVR5cGUgPSBjYXNlX3doZW4odG9wb2dyYXBoeSA9PSAnQScgfiAnQWJvdmUgU3RyZWV0IExldmVsIFRvcG9ncmFwaHknLA0KICB0b3BvZ3JhcGh5ID09ICdCJyB+ICdCZWxvdyBTdHJlZXQgTGV2ZWwgVG9wb2dyYXBoeScsDQogIHRvcG9ncmFwaHkgPT0gJ0MnIH4gJ0Zsb29kIFBsYWluIFRvcG9ncmFwaHknLA0KICB0b3BvZ3JhcGh5ID09ICdEJyB+ICdSb2NreSBUb3BvZ3JhcGh5JywNCiAgdG9wb2dyYXBoeSA9PSAnRScgfiAnT3RoZXIgVG9wb2dyYXBoeScsDQogIHRvcG9ncmFwaHkgPT0gJ0YnIH4gJ0xldmVsIFRvcG9ncmFwaHknKSkNCg0KIyBBc3NpZ25pbmcgdmFsdWUgdG8gTkEgcm93cyB0byBuYXR1cmUgb2YgdmlldyB1bmtub3duDQoNCmRhdGEkVG9wb2dyYXBoeVR5cGVbaXMubmEoZGF0YSRUb3BvZ3JhcGh5VHlwZSldIDwtICJUb3BvZ3JhcGh5IFVua25vd24iDQpkYXRhJHRvcG9ncmFwaHlbaXMubmEoZGF0YSR0b3BvZ3JhcGh5KV0gPC0gMA0KDQojIyBDYXRlZ29yaXppbmcgYmFzZWQgb24gUGFyY2VsIFR5cGUNCg0KZGF0YSA8LSBkYXRhICU+JQ0KICBtdXRhdGUoUGFyY2VsVHlwZSA9IGNhc2Vfd2hlbihwYXJjZWxfc2hhcGUgPT0gJ0EnIH4gJ0lycmVndWxhciBQYXJjZWwnLA0KICBwYXJjZWxfc2hhcGUgPT0gJ0InIH4gJ0dyb3NzbHkgSXJyZWd1bGFyIFBhcmNlbCcsDQogIHBhcmNlbF9zaGFwZSA9PSAnQycgfiAnVHJpYW5ndWxhciBQYXJjZWwnLA0KICBwYXJjZWxfc2hhcGUgPT0gJ0QnIH4gJ0xvbmcgTmFycm93IFBhcmNlbCcsDQogIHBhcmNlbF9zaGFwZSA9PSAnRScgfiAnUmVjdGFuZ3VsYXIgUGFyY2VsJykpDQoNCiMgQXNzaWduaW5nIHZhbHVlIHRvIE5BIHJvd3MgdG8gbmF0dXJlIG9mIHZpZXcgdW5rbm93bg0KDQpkYXRhJFBhcmNlbFR5cGVbaXMubmEoZGF0YSRQYXJjZWxUeXBlKV0gPC0gIlBhcmNlbCBUeXBlIFVua25vd24iDQpkYXRhJHBhcmNlbF9zaGFwZVtpcy5uYShkYXRhJHBhcmNlbF9zaGFwZSldIDwtIDANCg0KDQpgYGANCg0KIyMjIEltcHV0aW5nIE1pc3NpbmcgRGF0YSANCg0KQW4gaW5pdGlhbCBhbmFseXNpcyBvZiB0aGUgZGF0YXNldCBpbmRpY2F0ZWQgbWlzc2luZyB2YWx1ZXMgZm9yIGEgbnVtYmVyIG9mIHJvd3Mgd2l0aGluIGltcG9ydGFudCBmYWN0b3JzIHRoYXQgaW5mb3JtIHNhbGUgcHJpY2VzIHN1Y2ggYXMgbnVtYmVyIG9mIGJlZHJvb21zLCBudW1iZXIgb2YgYmF0aHJvb21zLCBhbmQgbnVtYmVyIG9mIHJvb21zLiBBIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIGlzIHVzZWQgdG8gcHJlZGljdCB0aGVzZSBtaXNzaW5nIHZhbHVlcyBiYXNlZCBvbiB0aGUgcmVsYXRpb25zaGlwcyBvYnNlcnZlZCBpbiB0aGUgYXZhaWxhYmxlIGRhdGEuIA0KDQpJbiB0aGUgZ2l2ZW4gZGF0YXNldCwgYSBzdHJvbmcgcmVsYXRpb24gaXMgb2JzZXJ2ZWQgYmV0d2VlbiBudW1iZXIgb2Ygcm9vbXMgYW5kIHRvdGFsIGxpdmFibGUgYXJlYS4gSGVyZSwgdGhlIGxpbmVhciByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGVzZSB0d28gZmFjdG9ycyBhcmUgc3R1ZGllZCBhY3Jvc3MgdGhlIGRhdGFzZXQgYW5kIHByb2plY3RlZCB0byB0aGUgcm93cyB3aXRoIHRoZSBtaXNzaW5nIHZhbHVlcy4gVGhlIGFzc3VtcHRpb24gaGVyZSBpcyB0aGF0LCB3aGVuIHBsb3R0ZWQgb24gYSBncmFwaCwgdGhlIG1pc3NpbmcgdmFsdWVzIHdpbGwgaGF2ZSB0aGUgc2FtZSBzbG9wZSBhbmQgaW50ZXJjZXB0IGFzIHRoZSByZXN0IG9mIHRoZSBkYXRhc2V0LiANCg0KVGhpcyBpbXB1dGluZyBwcmFjdGljZSBpcyBleHRlbmRlZCB0byBvdGhlciBmaWVsZHMgd2l0aCBtaXNzaW5nIHZhbHVlcyBzdWNoIGFzIG51bWJlciBvZiBiYXRocm9vbXMgYW5kIG51bWJlciBvZiBiZWRyb29tcy4NCg0KIyMjIyBCZWRyb29tcw0KDQpgYGB7ciBJbXB1dGluZyB2YWx1ZXMgZm9yIG1pc3NpbmcgdmFsdWVzIG9mIG51bWJlciBvZiBiZWRyb29tcywgd2FybmluZz1GQUxTRSwgcmVzdWx0cz0naGlkZSd9DQoNCiMjIEltcHV0aW5nIHZhbHVlcyBmb3IgbWlzc2luZyB2YWx1ZXMgb2YgbnVtYmVyIG9mIGJlZHJvb21zIGJhc2VkIG9uIHRvdGFsIGxpdmFibGUgYXJlYQ0KDQojIERyb3BwaW5nIDMyIHZhbHVlcyBvZiB0b3RhbCBsaXZhYmxlIGFyZWEgd2hpY2ggYXJlIDAgZm9yIGJldHRlciBwcmVkaWN0aW9uDQoNCmRhdGEgPC0gIGRhdGEgJT4lIA0KICBmaWx0ZXIodG90YWxfbGl2YWJsZV9hcmVhID4gMCApDQoNCiMgU3RlcCAxIC0gQ3JlYXRpbmcgYW4gaW5kZXggb2YgMCBhbmQgMSB2YWx1ZXMgZm9yIHJvd3MgdGhhdCBoYXZlIHZhbHVlcyBmb3IgbnVtYmVyIG9mIGJhdGhyb29tcyBhbmQgcm93cyB0aGF0IGRvIG5vdA0KDQpkYXRhIDwtIGRhdGEgJT4lDQogIG11dGF0ZShCZWRyb29tSW5kZXggPSBjYXNlX3doZW4obnVtYmVyX29mX2JlZHJvb21zID49IDEgfiAxLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG51bWJlcl9vZl9iZWRyb29tcyA8IDEgfiAwKSkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCmRhdGEkQmVkcm9vbUluZGV4W2lzLm5hKGRhdGEkQmVkcm9vbUluZGV4KV0gPC0gMA0KDQojIFN0ZXAgMiAtIENyZWF0aW5nIGEgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgcmVsYXRpbmcgbnVtYmVyIG9mIGJlZHJvb21zIGFuZCB0b3RhbCBsaXZlYWJsZSBhcmVhDQoNCmxtKG51bWJlcl9vZl9iZWRyb29tcyB+IHRvdGFsX2xpdmFibGVfYXJlYSwgZGF0YT1kYXRhKQ0KDQojIFN0ZXAgMyAtIEltcHV0aW5nIG5ldyB2YWx1ZXMgZm9yIG1pc3NpbmcgdmFsdWVzIG9mIG51bWJlciBvZiBiZWRyb29tcyB1c2luZyByZWdyZXNzaW9uIHJlc3VsdHMNCg0KZm9yKGkgaW4gMTpucm93KGRhdGEpKQ0Kew0KICBpZiAoZGF0YSRCZWRyb29tSW5kZXhbaV0gPT0gMCkNCiAgew0KICAgIGRhdGEkbnVtYmVyX29mX2JlZHJvb21zW2ldID0gMi4xNjA1NjUwICsgMC4wMDAzMDU3KmRhdGEkdG90YWxfbGl2YWJsZV9hcmVhW2ldDQogIH0NCiAgfQ0KDQpkYXRhJG51bWJlcl9vZl9iZWRyb29tcyA8LSByb3VuZChkYXRhJG51bWJlcl9vZl9iZWRyb29tcywgZGlnaXRzPTApDQoNCmBgYA0KDQojIyMjIFJvb21zIA0KDQpgYGB7ciBJbXB1dGluZyB2YWx1ZXMgZm9yIG1pc3NpbmcgdmFsdWVzIG9mIG51bWJlciBvZiByb29tcywgd2FybmluZz1GQUxTRSwgcmVzdWx0cz0naGlkZSd9DQoNCiMjIEltcHV0aW5nIHZhbHVlcyBmb3IgbWlzc2luZyB2YWx1ZXMgb2YgbnVtYmVyIG9mIHJvb21zIGJhc2VkIG9uIHRvdGFsIGxpdmFibGUgYXJlYQ0KDQojIFN0ZXAgMSAtIENyZWF0aW5nIGFuIGluZGV4IG9mIDAgYW5kIDEgdmFsdWVzIGZvciByb3dzIHRoYXQgaGF2ZSB2YWx1ZXMgZm9yIG51bWJlciBvZiByb29tcyBhbmQgcm93cyB0aGF0IGRvIG5vdA0KDQpkYXRhIDwtIGRhdGEgJT4lDQogIG11dGF0ZShSb29tSW5kZXggPSBjYXNlX3doZW4obnVtYmVyX29mX3Jvb21zID49IDEgfiAxLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG51bWJlcl9vZl9yb29tcyA8IDEgfiAwKSkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCmRhdGEkUm9vbUluZGV4W2lzLm5hKGRhdGEkUm9vbUluZGV4KV0gPC0gMA0KDQojIFN0ZXAgMiAtIENyZWF0aW5nIGEgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgcmVsYXRpbmcgbnVtYmVyIG9mIHJvb21zIGFuZCB0b3RhbCBsaXZhYmxlIGFyZWENCg0KbG0obnVtYmVyX29mX3Jvb21zIH4gdG90YWxfbGl2YWJsZV9hcmVhLCBkYXRhPWRhdGEpDQoNCiMgU3RlcCAzIC0gSW1wdXRpbmcgbmV3IHZhbHVlcyBmb3IgbWlzc2luZyB2YWx1ZXMgb2YgbnVtYmVyIG9mIHJvb21zIHVzaW5nIHJlZ3Jlc3Npb24gcmVzdWx0cw0KDQpmb3IoaSBpbiAxOm5yb3coZGF0YSkpDQp7DQogIGlmIChkYXRhJFJvb21JbmRleFtpXSA9PSAwKQ0KICB7DQogICAgZGF0YSRudW1iZXJfb2Zfcm9vbXNbaV0gPSA0LjMxNjUwMyArIDAuMDAxMzE5KmRhdGEkdG90YWxfbGl2YWJsZV9hcmVhW2ldDQogIH0NCiAgfQ0KDQpkYXRhJG51bWJlcl9vZl9yb29tcyA8LSByb3VuZChkYXRhJG51bWJlcl9vZl9yb29tcywgZGlnaXRzPTApDQoNCmBgYA0KDQojIyMjIEJhdGhyb29tcw0KDQpgYGB7ciBJbXB1dGluZyB2YWx1ZXMgZm9yIG1pc3NpbmcgdmFsdWVzIG9mIG51bWJlciBvZiBiYXRocm9vbXMsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0KDQojIyBJbXB1dGluZyB2YWx1ZXMgZm9yIG1pc3NpbmcgdmFsdWVzIG9mIG51bWJlciBvZiBiYXRocm9vbXMgYmFzZWQgb24gdG90YWwgbGl2YWJsZSBhcmVhDQoNCiMgU3RlcCAxIC0gQ3JlYXRpbmcgYW4gaW5kZXggb2YgMCBhbmQgMSB2YWx1ZXMgZm9yIHJvd3MgdGhhdCBoYXZlIHZhbHVlcyBmb3IgbnVtYmVyIG9mIGJhdGhyb29tcyBhbmQgcm93cyB0aGF0IGRvIG5vdA0KDQpkYXRhIDwtIGRhdGEgJT4lDQogIG11dGF0ZShCYXRocm9vbUluZGV4ID0gY2FzZV93aGVuKG51bWJlcl9vZl9iYXRocm9vbXMgPj0gMSB+IDEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnVtYmVyX29mX2JhdGhyb29tcyA8IDEgfiAwKSkNCg0KZGF0YSRCYXRocm9vbUluZGV4W2lzLm5hKGRhdGEkQmF0aHJvb21JbmRleCldIDwtIDANCg0KIyBTdGVwIDIgLSBDcmVhdGluZyBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIHJlbGF0aW5nIG51bWJlciBvZiBiYXRocm9vbXMgYW5kICB0b3RhbCBsaXZhYmxlIGFyZWENCg0KbG0obnVtYmVyX29mX2JhdGhyb29tcyB+IHRvdGFsX2xpdmFibGVfYXJlYSwgZGF0YT1kYXRhKQ0KDQojIFN0ZXAgMyAtIEltcHV0aW5nIG5ldyB2YWx1ZXMgZm9yIG1pc3NpbmcgdmFsdWVzIG9mIG51bWJlciBvZiBiYXRocm9vbXMgdXNpbmcgcmVncmVzc2lvbiByZXN1bHRzDQoNCmZvcihpIGluIDE6bnJvdyhkYXRhKSkNCnsNCiAgaWYgKGRhdGEkQmF0aHJvb21JbmRleFtpXSA9PSAwKQ0KICB7DQogICAgZGF0YSRudW1iZXJfb2ZfYmF0aHJvb21zW2ldID0gMC42NDI2MzEwICsgMC4wMDAzMjgzKmRhdGEkdG90YWxfbGl2YWJsZV9hcmVhW2ldDQogIH0NCiAgfQ0KDQpkYXRhJG51bWJlcl9vZl9iYXRocm9vbXMgPC0gcm91bmQoZGF0YSRudW1iZXJfb2ZfYmF0aHJvb21zLCBkaWdpdHM9MCkNCg0KYGBgDQoNCiMjIyBDcmVhdGluZyBOZXcgVmFyaWFibGVzIA0KDQpQb3N0IGNsZWFuaW5nLCB0d28gbmV3IHZhcmlhYmxlcyBhcmUgY3JlYXRlZCB3aXRoaW4gdGhlIGRhdGFzZXQgdG8gZXZhbHVhdGUgdGhlIHByaWNlIHBlciBzcXVhcmUgZm9vdCB3aXRoaW4gc29sZCBwcm9wZXJ0aWVzIGFzIHdlbGwgYXMgdGhlIGFnZSBvZiB0aGUgc29sZCBwcm9wZXJ0aWVzLiANCg0KYGBge3IgUHJpY2UvU3FmdGEsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0KDQpkYXRhIDwtIA0KICBkYXRhICU+JQ0KICBtdXRhdGUoUHJpY2VQZXJTcWZ0ID0gKHNhbGVfcHJpY2UvdG90YWxfbGl2YWJsZV9hcmVhKSkNCg0KZGF0YSA8LSANCiAgZGF0YSAlPiUNCiAgbXV0YXRlKEJ1aWxkaW5nQWdlID0gKDIwMjMgLSAoeWVhcl9idWlsdCkpKQ0KDQpgYGANCg0KIyMjIERyb3BwaW5nIENvbHVtbnMgYW5kIE91dGxpZXJzIA0KDQpBcyB0aGUgbGFzdCBzdGVwLCB0aGUgZm91bmRhdGlvbmFsIGRhdGFzZXQgY2FycnlpbmcgaW5mb3JtYXRpb24gYWJvdXQgdGhlIHByb3BlcnRpZXMgaXMgZmlsdGVyZWQgdG8gb25seSBob2xkIHRoZSBpbmRpY2F0aXZlIGludGVybmFsIHZhcmlhYmxlcyB3aGljaCBtYXkgcGxheSBhIHJvbGUgaW4gaW5mbHVlbmNpbmcgdGhlIGhvbWUgcHJpY2UgdmFsdWUuIFRvIGFjaGlldmUgYmV0dGVyIGFjY3VyYWN5IGFuZCBmZXdlciBlcnJvcnMgaW4gcHJlZGljdGlvbiwgb3V0bGllciB2YWx1ZXMgaW4gdGhlIGRhdGFzZXQgYXJlIGVsaW1pbmF0ZWQgdG8gcmVtb3ZlIGFueSBzaWduaWZpY2FudCBza2V3cyBpbiB0aGUgZGF0YXNldC4NCg0KDQpgYGB7ciBmaWx0ZXIgZGF0YSwgd2FybmluZz1GQUxTRSwgcmVzdWx0cz0naGlkZSd9DQoNCg0KZGF0YSA8LSBkYXRhICU+JSANCiAgc2VsZWN0KG9iamVjdGlkLCBhc3Nlc3NtZW50X2RhdGUsIEJ1aWxkaW5nQWdlLCB5ZWFyX2J1aWx0LCBidWlsZGluZ19jb2RlLCBidWlsZGluZ19jb2RlX2Rlc2NyaXB0aW9uLCBwaW4sIGJ1aWxkaW5nX2NvZGVfbmV3LCBidWlsZGluZ19jb2RlX2Rlc2NyaXB0aW9uX25ldywgIGNlbnN1c190cmFjdCwgZ2VvZ3JhcGhpY193YXJkLCB6b25pbmcsIGxvY2F0aW9uLCBzdHJlZXRfbmFtZSwgc3RyZWV0X2NvZGUsIHN0cmVldF9kZXNpZ25hdGlvbiwgemlwX2NvZGUsIGhvdXNlX251bWJlciwgZGVwdGgsIGZyb250YWdlLCBjZW50cmFsX2FpciwgZmlyZXBsYWNlcywgZnVlbCwgRnVlbFR5cGUsIGJhc2VtZW50cywgQmFzZW1lbnRQcmVzZW50LCBCYXNlbWVudFR5cGUsIGdhcmFnZV9zcGFjZXMsIEdhcmFnZVByZXNlbnQsIEdhcmFnZVR5cGUsIGV4dGVyaW9yX2NvbmRpdGlvbiwgRXh0ZXJpb3JDb25kaXRpb25UeXBlLCBpbnRlcmlvcl9jb25kaXRpb24sIEludGVyaW9yQ29uZGl0aW9uVHlwZSwgbnVtYmVyX29mX2JhdGhyb29tcywgbnVtYmVyX29mX2JlZHJvb21zLCBudW1iZXJfb2Zfcm9vbXMsIG51bWJlcl9zdG9yaWVzLCB0b3RhbF9saXZhYmxlX2FyZWEsIHZpZXdfdHlwZSwgVmlld1R5cGUsIHRvcG9ncmFwaHksIFRvcG9ncmFwaHlUeXBlLCBwYXJjZWxfc2hhcGUsIFBhcmNlbFR5cGUsIHNhbGVfZGF0ZSwgc2FsZV95ZWFyLCBzYWxlX3ByaWNlLCBQcmljZVBlclNxZnQsIG11c2FJRCwgdG9QcmVkaWN0LCBnZW9tZXRyeSApDQpgYGANCg0KYGBge3IgZmlsdGVyIG91dGxpZXJzLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30NCg0KZGF0YSA8LSAgZGF0YSAlPiUgDQogIGZpbHRlcihudW1iZXJfb2ZfYmVkcm9vbXMgPDEwLCBudW1iZXJfb2Zfcm9vbXM8MTUsICgobnVtYmVyX29mX2JhdGhyb29tcytudW1iZXJfb2ZfYmVkcm9vbXMpIDwgbnVtYmVyX29mX3Jvb21zKSwgUHJpY2VQZXJTcWZ0PDE1MDAsIHRvdGFsX2xpdmFibGVfYXJlYTwxMDAwMCkNCg0KYGBgDQoNCg0KIyMgQ2Vuc3VzIERhdGENCg0KQWZ0ZXIgY2xlYW5pbmcgdGhlIHByaW1hcnkgZGF0YXNldCBhbmQgaW1wb3J0aW5nIGRhdGEgb24gc3Vycm91bmRpbmcgcGh5c2ljYWwgYW1lbml0aWVzLCB0aGUgbmV4dCBzdGVwIGlzIGdhdGhlcmluZyByZWxldmFudCBjZW5zdXMgZGF0YSB0byB1bmRlcnN0YW5kIGRlbW9ncmFwaGljIGNvbmRpdGlvbnMgcGVydGFpbmluZyB0byB0aGVzZSBkYXRhIHBvaW50cy4gVGhlIHByb3ZpZGVkIFIgY29kZSBjaHVuayBmZXRjaGVzIGFuZCBwcm9jZXNzZXMgZGF0YSBmcm9tIHRoZSBBbWVyaWNhbiBDb21tdW5pdHkgU3VydmV5IChBQ1MpIGZvciBQaGlsYWRlbHBoaWEgY2Vuc3VzIHRyYWN0cyBpbiB0aGUgeWVhciAyMDIxLiBJdCByZXRyaWV2ZXMgdmFyaW91cyBkZW1vZ3JhcGhpYyBhbmQgc29jaW9lY29ub21pYyB2YXJpYWJsZXMgZm9yIHRoZXNlIHRyYWN0cyBhbmQgcGVyZm9ybXMgc29tZSBkYXRhIHRyYW5zZm9ybWF0aW9ucy4gVGhlIHllYXIgY2hvc2VuIGZvciBhbmFseXNpcyBpcyAyMDIxIHRvIGZhY3RvciByZWNvdmVyeSBmcm9tIENvdmlkIGluIG9yZGVyIHRvIG1ha2UgbW9yZSBhY2N1cmF0ZSBwcmVkaWN0aW9ucy4gDQoNCkEgbGlzdCBvZiB2YXJpYWJsZXMgaW5jbHVkaW5nIHBvcHVsYXRpb24sIGluY29tZSwgaG91c2luZy1yZWxhdGVkIGluZm9ybWF0aW9uLCBlZHVjYXRpb24sIHBvdmVydHkgbGV2ZWxzLCBhbmQgcmFjaWFsL2V0aG5pYyBkZW1vZ3JhcGhpY3MgYXJlIGdhdGhlcmVkIGFuZCB0cmFuc2Zvcm1lZCB0byBiZSBwcm9qZWN0ZWQgaW4gdGhlIEVTUkk6MTAyNzI4IGNvb3JkaW5hdGUgc3lzdGVtLiBUaGlzIGluZm9ybWF0aW9uIGlzIHN0b3JlZCBpbiBhIG5ldyBkYXRhc2V0IC0gJ3RyYWN0czIxJy4NCg0KDQpgYGB7ciBjZW5zdXMsIGNhY2hlPVRSVUUsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9ICdoaWRlJ30NCg0KYWNzX3ZhcmlhYmxlX2xpc3QuMjAyMSA8LSBsb2FkX3ZhcmlhYmxlcygyMDIxLCAjeWVhcg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYWNzNSIsICNmaXZlIHllYXIgQUNTIGVzdGltYXRlcw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYWNoZSA9IFRSVUUpDQojIDIwMjEsIEENCg0KIyBSZXRyaWV2ZSBBQ1MgZGF0YSBmb3IgUGhpbGFkZWxwaGlhIHRyYWN0cyBpbiAyMDIwDQp0cmFjdHMyMSA8LSBnZXRfYWNzKA0KICBnZW9ncmFwaHkgPSAidHJhY3QiLA0KICB2YXJpYWJsZXMgPSBjKA0KICAgICJCMDEwMDNfMDAxIiwgICAjIFRvdGFsIFBvcHVsYXRpb24NCiAgICAiQjE5MDEzXzAwMSIsICAgIyBNZWRpYW4gSG91c2Vob2xkIEluY29tZQ0KICAgICJCMjUwNThfMDAxIiwgICAjIE1lZGlhbiBSZW50DQogICAgIkIyNTAwOF8wMDIiLCAgICMgT3duZXItT2NjdXBpZWQgVW5pdHMNCiAgICAiQjI1MDA4XzAwMyIsICAgIyBSZW50ZXItT2NjdXBpZWQgVW5pdHMNCiAgICAiQjA3MDAxXzAzMiIsICAgIyBTYW1lIEhvdXNlIDc1IFllYXJzIEFnbw0KICAgICJCMDcwMDFfMDE3IiwgICAjIFNhbWUgSG91c2UgMSBZZWFyIEFnbw0KICAgICJCMjUwODhfMDAzIiwgICAjIE1lZGlhbiBTZWxlY3RlZCBNb250aGx5IE93bmVyIENvc3RzIChob21lcyB3aXRob3V0IGEgbW9ydGdhZ2UpDQogICAgIkIyNTA4OF8wMDIiLCAgICMgTWVkaWFuIFNlbGVjdGVkIE1vbnRobHkgT3duZXIgQ29zdHMgKGhvbWVzIHdpdGggYSBtb3J0Z2FnZSkNCiAgICAiQjI1MDY0XzAwMSIsICAgIyBNZWRpYW4gR3Jvc3MgUmVudCAocmVudCBhbmQgdXRpbGl0aWVzKQ0KICAgICJCMjUxMTdfMDAxIiwgICAjIFBlcmNlbnRhZ2Ugb2YgSG91c2luZyBVbml0cyB3aXRoIGhlYXQNCiAgICAiQjE1MDAzXzAyMiIsICAgIyBFZHVjYXRpb25hbCBBdHRhaW5tZW50OiBCYWNoZWxvcidzIERlZ3JlZQ0KICAgICJCMTcwMDFfMDAyIiwgICAjIFBlcmNlbnRhZ2Ugb2YgUG9wdWxhdGlvbiBCZWxvdyB0aGUgUG92ZXJ0eSBMZXZlbA0KICAgICJCMjgwMDJfMDA0IiwgICAjIFBlcmNlbnRhZ2Ugb2YgSG91c2luZyBVbml0cyB3aXRoIEhpZ2gtU3BlZWQgSW50ZXJuZXQNCiAgICAiQjI1MDQ0XzAwMyIsICAgIyBQZXJjZW50YWdlIG9mIEhvdXNpbmcgVW5pdHMgd2l0aCBObyBWZWhpY2xlIEF2YWlsYWJsZQ0KICAgICJCMDIwMDFfMDAyIiwgICAjIFJhY2UgYW5kIEV0aG5pY2l0eTogV2hpdGUgQWxvbmUNCiAgICAiQjAyMDAxXzAwMyIsICAgIyBSYWNlIGFuZCBFdGhuaWNpdHk6IEJsYWNrIG9yIEFmcmljYW4gQW1lcmljYW4gQWxvbmUNCiAgICAiQjAzMDAxXzAwMyIgICAgIyBIaXNwYW5pYyBvciBMYXRpbm8gT3JpZ2luIG9mIFBvcHVsYXRpb24NCiAgKSwNCiAgeWVhciA9IDIwMjEsDQogIHN0YXRlID0gIlBBIiwNCiAgY291bnR5ID0gIlBoaWxhZGVscGhpYSIsDQogIGdlb21ldHJ5ID0gVFJVRSwNCiAgb3V0cHV0ID0gIndpZGUiDQopJT4lDQogIHNlbGVjdCgtTkFNRSwgLWVuZHNfd2l0aCgiTSIpKSAlPiUNCiAgcmVuYW1lKHRvdGFscG9wID0gQjAxMDAzXzAwMUUsICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBUb3RhbCBQb3B1bGF0aW9uDQogICAgICAgICBtZWRfaW5jb21lID0gQjE5MDEzXzAwMUUsICAgICAgICAgICAgICAgICAgICAgICAgICMgTWVkaWFuIEhvdXNlaG9sZCBJbmNvbWUNCiAgICAgICAgIG1lZF9yZW50ID0gQjI1MDU4XzAwMUUsICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBNZWRpYW4gUmVudA0KICAgICAgICAgb3duZXJfdW5pdHMgPSBCMjUwMDhfMDAyRSwgICAgICAgICAgICAgICAgICAgICAgICAjIE93bmVyLU9jY3VwaWVkIFVuaXRzDQogICAgICAgICByZW50ZXJfdW5pdHMgPSBCMjUwMDhfMDAzRSwgICAgICAgICAgICAgICAgICAgICAgICMgUmVudGVyLU9jY3VwaWVkIFVuaXRzDQogICAgICAgICBzYW1lX2hvdXNlXzc1ID0gQjA3MDAxXzAzMkUsICAgICAgICAgICAgICAgICAgICAgICMgU2FtZSBIb3VzZSA3NSBZZWFycyBBZ28NCiAgICAgICAgIHNhbWVfaG91c2VfMSA9IEIwNzAwMV8wMTdFLCAgICAgICAgICAgICAgICAgICAgICAgIyBTYW1lIEhvdXNlIDEgWWVhciBBZ28NCiAgICAgICAgIG1vbnRobHlfY29zdHNfbm9fbW9ydGdhZ2UgPSBCMjUwODhfMDAzRSwgICAgICAgICAgIyBNZWRpYW4gU2VsZWN0ZWQgTW9udGhseSBPd25lciBDb3N0cyAoaG9tZXMgd2l0aG91dCBhIG1vcnRnYWdlKQ0KICAgICAgICAgbW9udGhseV9jb3N0c193aXRoX21vcnRnYWdlID0gQjI1MDg4XzAwMkUsICAgICAgICAjIE1lZGlhbiBTZWxlY3RlZCBNb250aGx5IE93bmVyIENvc3RzIChob21lcyB3aXRoIGEgbW9ydGdhZ2UpDQogICAgICAgICBtZWRfZ3Jvc3NfcmVudCA9IEIyNTA2NF8wMDFFLCAgICAgICAgICAgICAgICAgICAgICMgTWVkaWFuIEdyb3NzIFJlbnQgKHJlbnQgYW5kIHV0aWxpdGllcykNCiAgICAgICAgIGhvdXNpbmdfdW5pdHNfd2l0aF9oZWF0ID0gQjI1MTE3XzAwMUUsICAgICAgICAgICAgIyBQZXJjZW50YWdlIG9mIEhvdXNpbmcgVW5pdHMgd2l0aCBoZWF0DQogICAgICAgICBlZHVfYmFjaGVsb3JzID0gQjE1MDAzXzAyMkUsICAgICAgICAgICAgICAgICAgICAgICMgRWR1Y2F0aW9uYWwgQXR0YWlubWVudDogQmFjaGVsb3IncyBEZWdyZWUNCiAgICAgICAgIHBvcF9iZWxvd19wb3ZlcnR5ID0gQjE3MDAxXzAwMkUsICAgICAgICAgICAgICAgICAgIyBQZXJjZW50YWdlIG9mIFBvcHVsYXRpb24gQmVsb3cgdGhlIFBvdmVydHkgTGV2ZWwNCiAgICAgICAgIGhvdXNpbmdfdW5pdHNfaGlnaF9zcGVlZF9pbnRlcm5ldCA9IEIyODAwMl8wMDRFLCAgIyBQZXJjZW50YWdlIG9mIEhvdXNpbmcgVW5pdHMgd2l0aCBIaWdoLVNwZWVkIEludGVybmV0DQogICAgICAgICBob3VzaW5nX3VuaXRzX25vX3ZlaGljbGUgPSBCMjUwNDRfMDAzRSwgICAgICAgICAgICMgUGVyY2VudGFnZSBvZiBIb3VzaW5nIFVuaXRzIHdpdGggTm8gVmVoaWNsZSBBdmFpbGFibGUNCiAgICAgICAgIHJhY2Vfd2hpdGUgPSBCMDIwMDFfMDAyRSwgICAgICAgICAgICAgICAgICAgICAgICAgIyBSYWNlIGFuZCBFdGhuaWNpdHk6IFdoaXRlIEFsb25lDQogICAgICAgICByYWNlX2JsYWNrID0gQjAyMDAxXzAwM0UsICAgICAgICAgICAgICAgICAgICAgICAgICMgUmFjZSBhbmQgRXRobmljaXR5OiBCbGFjayBvciBBZnJpY2FuIEFtZXJpY2FuIEFsb25lDQogICAgICAgICBoaXNwYW5pY19sYXRpbm8gPSBCMDMwMDFfMDAzRSAgICAgICAgICAgICAgICAgICAgICMgUmFjZSBhbmQgRXRobmljaXR5OiBIaXNwYW5pYyBvciBMYXRpbm8NCiAgICAgICAgICkNCg0KIyBUcmFuc2Zvcm0gdGhlIGRhdGEgdG8gRVNSSToxMDI3MjggcHJvamVjdGlvbg0KdHJhY3RzMjEgPC0gdHJhY3RzMjEgJT4lIHN0X3RyYW5zZm9ybShzdF9jcnMoZGF0YSkpDQoNCmBgYA0KDQojIyBPcGVuIERhdGEgcGhpbGx5IA0KDQpUbyBiZWdpbiBjb25zdHJ1Y3RpbmcgYSBwcmVkaWN0aXZlIG1vZGVsIGZvciB0aGUgUGhpbGFkZWxwaGlhIGhvdXNpbmcgbWFya2V0LCBhZGRpdGlvbmFsIGV4dGVybmFsIHZhcmlhYmxlcyBhcmUgaW5jb3Jwb3JhdGVkLiBUaGVzZSB2YXJpYWJsZXMgZXhhbWluZSB0aGUgcHJveGltaXR5IG9mIHZhcmlvdXMgYW1lbml0aWVzIGFuZCBwdWJsaWMgc2VydmljZXMgdG8gdGhlIGhvbWVzIGxpc3RlZCBpbiB0aGUgZm91bmRhdGlvbmFsIGRhdGFzZXQuIFRoZSBmb2xsb3dpbmcgY29kZSBpbXBvcnRzIGRhdGEgcmVsYXRlZCB0byB0aGVzZSBwb3RlbnRpYWxseSBpbmZsdWVudGlhbCBhbWVuaXRpZXMsIGluY2x1ZGluZyBpbmZvcm1hdGlvbiBhYm91dCB0aGUgbG9jYXRpb25zIG9mIHB1YmxpYyBhbmQgcHJpdmF0ZSBzY2hvb2xzLCBjb2xsZWdlcywgY2l0eSBsYW5kbWFya3MsIHBsYXlncm91bmRzLCB0cmFpbHMsIHRlbm5pcyBjb3VydHMsIHBvb2xzLCB0cmFuc2l0IHN0b3BzIGFuZCBwb2xpY2Ugc3RhdGlvbnMuIEFkZGl0aW9uYWxseSwgaW5mb3JtYXRpb24gb24gcG90ZW50aWFsIGhvbWUgdmFsdWUgaW5oaWJpdG9ycyBzdWNoIGFzIGxpdHRlciwgcHJveGltaXR5IHRvIHRoZSBmbG9vZHBsYWluIGFuZCBpbnN0YW5jZXMgb2YgZ3VuIHZpb2xlbmNlIGFyZSBhbHNvIGJyb3VnaHQgaW4uIFRoZXNlIGRhdGFzZXRzIGFyZSBzb3VyY2VkIGZyb20gW09wZW4gRGF0YSBQaGlsbHkuXShodHRwczovL2RhdGEtcGhsLm9wZW5kYXRhLmFyY2dpcy5jb20vKQ0KDQpgYGB7ciBBZGRpbmcgRGF0YSwgd2FybmluZz1GQUxTRSwgcmVzdWx0cz0gJ2hpZGUnfQ0KDQojIE5lYXJlc3QgU2Nob29scw0KIyMgQWRkaW5nIGRhdGEgb24gUGhpbGFkZWxwaGlhIFNjaG9vbHMNCg0KUGhpbGx5U2Nob29scyA8LQ0KICAgc3RfcmVhZCgiLi9kYXRhL1NjaG9vbHMuZ2VvanNvbiIpICU+JQ0KICBzdF90cmFuc2Zvcm0oc3RfY3JzKHRyYWN0czIxKSkNCg0KUGhpbGx5UHZ0U2Nob29scyA8LQ0KICAgc3RfcmVhZCgiLi9kYXRhL1NjaG9vbHMuZ2VvanNvbiIpICU+JQ0KICBmaWx0ZXIoVFlQRV9TUEVDSUZJQyA9PSAiUFJJVkFURSIpICU+JQ0KICBzdF90cmFuc2Zvcm0oc3RfY3JzKHRyYWN0czIxKSkNCg0KIyMgTWFwcGluZyBuZWFyZXN0IHNjaG9vbA0KDQpuZWFyZXN0X3NjaG9vbCA8LSBzZjo6c3RfbmVhcmVzdF9mZWF0dXJlKGRhdGEsIFBoaWxseVNjaG9vbHMpDQpuZWFyZXN0X3B2dF9zY2hvb2wgPC0gc2Y6OnN0X25lYXJlc3RfZmVhdHVyZShkYXRhLCBQaGlsbHlQdnRTY2hvb2xzKQ0KDQojIyBDb252ZXJ0aW5nIHNjaG9vbHMgdG8gcnNnZW8gZ2VvbWV0cmllcw0KDQp4IDwtIHJzZ2VvOjphc19yc2dlbyhkYXRhKQ0KeSA8LSByc2dlbzo6YXNfcnNnZW8oUGhpbGx5U2Nob29scykNCg0KIyMgQ2FsY3VsYXRpbmcgZGlzdGFuY2UNCg0KZGF0YSRkaXN0X3RvX25lYXJlc3Rfc2Nob29sIDwtIHJzZ2VvOjpkaXN0YW5jZV9ldWNsaWRlYW5fcGFpcndpc2UoeCwgeVtuZWFyZXN0X3NjaG9vbF0pDQoNCiMjIENvbnZlcnRpbmcgcHJpdmF0ZSBzY2hvb2xzIHRvIHJzZ2VvIGdlb21ldHJpZXMNCg0KeCA8LSByc2dlbzo6YXNfcnNnZW8oZGF0YSkNCnkgPC0gcnNnZW86OmFzX3JzZ2VvKFBoaWxseVB2dFNjaG9vbHMpDQoNCiMjIENhbGN1bGF0aW5nIGRpc3RhbmNlDQoNCmRhdGEkZGlzdF90b19uZWFyZXN0X3B2dF9zY2hvb2wgPC0gcnNnZW86OmRpc3RhbmNlX2V1Y2xpZGVhbl9wYWlyd2lzZSh4LCB5W25lYXJlc3RfcHZ0X3NjaG9vbF0pDQoNCiMgTmVhcmVzdCBDb2xsZWdlcw0KIyMgQWRkaW5nIGRhdGEgb24gUGhpbGFkZWxwaGlhIENvbGxlZ2VzDQoNClBoaWxseUNvbGxlZ2VzIDwtDQogc3RfcmVhZCgiLi9kYXRhL1VuaXZlcnNpdGllc19Db2xsZWdlcy5nZW9qc29uIiklPiUNCiAgc3RfdHJhbnNmb3JtKHN0X2Nycyh0cmFjdHMyMSkpDQoNCiMjIE1hcHBpbmcgbmVhcmVzdCBjb2xsZWdlDQoNCm5lYXJlc3RfY29sbGVnZSA8LSBzZjo6c3RfbmVhcmVzdF9mZWF0dXJlKGRhdGEsIFBoaWxseUNvbGxlZ2VzKQ0KDQojIyBDb252ZXJ0aW5nIHRvIHJzZ2VvIGdlb21ldHJpZXMNCg0KeCA8LSByc2dlbzo6YXNfcnNnZW8oZGF0YSkNCnkgPC0gcnNnZW86OmFzX3JzZ2VvKFBoaWxseUNvbGxlZ2VzKQ0KDQojIyBDYWxjdWxhdGluZyBkaXN0YW5jZQ0KDQpkYXRhJGRpc3RfdG9fbmVhcmVzdF9jb2xsZWdlIDwtIHJzZ2VvOjpkaXN0YW5jZV9ldWNsaWRlYW5fcGFpcndpc2UoeCwgeVtuZWFyZXN0X2NvbGxlZ2VdKQ0KDQojIE5lYXJlc3QgTGFuZG1hcmtzDQojIyBBZGRpbmcgZGF0YSBvbiBQaGlsYWRlbHBoaWEgbGFuZG1hcmtzIA0KDQpQaGlsbHlMYW5kbWFya3MgPC0NCiBzdF9yZWFkKCJodHRwczovL3NlcnZpY2VzLmFyY2dpcy5jb20vZkxlR2piN3U0dVhxZUY5cS9hcmNnaXMvcmVzdC9zZXJ2aWNlcy9MYW5kbWFya19Qb2ludHMvRmVhdHVyZVNlcnZlci8wL3F1ZXJ5P291dEZpZWxkcz0qJndoZXJlPTElM0QxJmY9Z2VvanNvbiIpJT4lDQogIHN0X3RyYW5zZm9ybShzdF9jcnModHJhY3RzMjEpKQ0KDQojIyBNYXBwaW5nIG5lYXJlc3QgbGFuZG1hcmsNCg0KbmVhcmVzdF9sYW5kbWFyayA8LSBzZjo6c3RfbmVhcmVzdF9mZWF0dXJlKGRhdGEsIFBoaWxseUxhbmRtYXJrcykNCg0KIyMgQ29udmVydGluZyB0byByc2dlbyBnZW9tZXRyaWVzDQoNCnggPC0gcnNnZW86OmFzX3JzZ2VvKGRhdGEpDQp5IDwtIHJzZ2VvOjphc19yc2dlbyhQaGlsbHlMYW5kbWFya3MpDQoNCiMjIENhbGN1bGF0aW5nIGRpc3RhbmNlDQoNCmRhdGEkZGlzdF90b19uZWFyZXN0X2xhbmRtYXJrIDwtIHJzZ2VvOjpkaXN0YW5jZV9ldWNsaWRlYW5fcGFpcndpc2UoeCwgeVtuZWFyZXN0X2xhbmRtYXJrXSkNCg0KIyBOZWFyZXN0IENvbW1lcmNpYWwgQ29ycmlkb3JzDQojIyBBZGRpbmcgZGF0YSBvbiBQaGlsYWRlbHBoaWEgQ29tbWVyY2lhbCBDb3JyaWRvcnMgDQoNClBoaWxseUNvbUNvcnIgPC0NCiAgc3RfcmVhZCgiLi9kYXRhL0NvbW1lcmNpYWxfQ29ycmlkb3JzLmdlb2pzb24iKSAlPiUNCiAgc3RfdHJhbnNmb3JtKHN0X2Nycyh0cmFjdHMyMSkpDQoNCiMjIElzIGl0IHdpdGhpbiB0aGUgY29tbWVyY2lhbCBjb3JyaWRvcj8NCg0KZGF0YSR3aXRoaW5fY29tX2NvcnIgPC0gaWZlbHNlKHN0X3dpdGhpbihkYXRhLCBQaGlsbHlDb21Db3JyKSwgMSwgMCkNCg0KZGF0YSA8LSBkYXRhICU+JQ0KICBtdXRhdGUod2l0aGluX2NvbV9jb3JyID0gaWZlbHNlKGlzLm5hKHdpdGhpbl9jb21fY29yciksIDAsIDEpKQ0KDQojIyBNYXBwaW5nIG5lYXJlc3QgY29tbWVyY2lhbCBjb3JyaWRvcg0KDQpuZWFyZXN0X2NvcnJpZG9yIDwtIHNmOjpzdF9uZWFyZXN0X2ZlYXR1cmUoZGF0YSwgUGhpbGx5Q29tQ29ycikNCg0KIyMgQ29udmVydGluZyB0byByc2dlbyBnZW9tZXRyaWVzDQoNCnggPC0gcnNnZW86OmFzX3JzZ2VvKGRhdGEpDQp5IDwtIHJzZ2VvOjphc19yc2dlbyhQaGlsbHlDb21Db3JyKQ0KDQojIyBDYWxjdWxhdGluZyBkaXN0YW5jZQ0KDQpkYXRhJGRpc3RfdG9fY29tbV9jb3JyIDwtIHJzZ2VvOjpkaXN0YW5jZV9ldWNsaWRlYW5fcGFpcndpc2UoeCwgeVtuZWFyZXN0X2NvcnJpZG9yXSkNCg0KIyBMaXR0ZXIgSW5kZXgNCiMjIEFkZGluZyBkYXRhIG9uIFBoaWxhZGVscGhpYSdzIExpdHRlciBJbmRleA0KDQpQaGlsbHlMaXR0ZXIgPC0NCiAgc3RfcmVhZCgiLi9kYXRhL0xpdHRlcl9JbmRleC5nZW9qc29uIikgJT4lDQogIHN0X3RyYW5zZm9ybShzdF9jcnModHJhY3RzMjEpKQ0KDQojIyBKb2luaW5nIHRoZSBsaXR0ZXIgc2NvcmUNCg0KZGF0YSA8LSANCiBzdF9qb2luKGRhdGEsKFBoaWxseUxpdHRlciAlPiUNCiAgICAgICAgICBzZWxlY3QoLU9CSkVDVElELCAtU2hhcGVfX0FyZWEsIC1TaGFwZV9fTGVuZ3RoICklPiUNCiAgICAgICAgICByZW5hbWUobGl0dGVyID0gU0NPUkUpKSkgDQoNCiNOZWFyZXN0IEZsb29kIFBsYWlucw0KIyMgQWRkaW5nIGRhdGEgb24gUGhpbGFkZWxwaGlhJ3MgRmxvb2QgUGxhaW4NCg0KUGhpbGx5Rmxvb2QgPC0gDQogIHN0X3JlYWQoIi4vZGF0YS9GRU1BXzEwMF9mbG9vZF9QbGFpbi5nZW9qc29uIikgJT4lDQogIHN0X3RyYW5zZm9ybShzdF9jcnModHJhY3RzMjEpKQ0KDQojIyBJcyBpdCB3aXRoaW4gdGhlIGZsb29kcGxhaW4/DQoNCmRhdGEkd2l0aGluX2Zsb29kIDwtIGlmZWxzZShzdF93aXRoaW4oZGF0YSwgUGhpbGx5Rmxvb2QpLCAxLCAwKQ0KDQpkYXRhIDwtIGRhdGEgJT4lDQogIG11dGF0ZSh3aXRoaW5fZmxvb2QgPSBpZmVsc2UoaXMubmEod2l0aGluX2Zsb29kKSwgMCwgMSkpDQoNCiMjIE1hcHBpbmcgbmVhcmVzdCBmbG9vZHBsYWluIA0KDQpuZWFyZXN0X2Zsb29kcGxhaW4gPC0gc2Y6OnN0X25lYXJlc3RfZmVhdHVyZShkYXRhLCBQaGlsbHlGbG9vZCkNCg0KIyMgQ29udmVydGluZyB0byByc2dlbyBnZW9tZXRyaWVzDQoNCnggPC0gcnNnZW86OmFzX3JzZ2VvKGRhdGEpDQp5IDwtIHJzZ2VvOjphc19yc2dlbyhQaGlsbHlGbG9vZCkNCg0KIyMgQ2FsY3VsYXRpbmcgZGlzdGFuY2UNCg0KZGF0YSRkaXN0X3RvX2Zsb29kcGxhaW4gPC0gcnNnZW86OmRpc3RhbmNlX2V1Y2xpZGVhbl9wYWlyd2lzZSh4LCB5W25lYXJlc3RfZmxvb2RwbGFpbl0pDQoNCiMgTmVhcmVzdCBUcmFuc2l0IFN0b3BzDQojIyBBZGRpbmcgZGF0YSBvbiBQaGlsYWRlbHBoaWEncyBUcmFuc2l0IFN0b3BzDQoNCmVsIDwtIHN0X3JlYWQoImh0dHBzOi8vb3BlbmRhdGEuYXJjZ2lzLmNvbS9kYXRhc2V0cy84YzZlMjU3NWM4YWQ0NmViODg3ZTZiYjM1ODI1ZTFhNl8wLmdlb2pzb24iKQ0KQnJvYWRfU3QgPC0gc3RfcmVhZCgiaHR0cHM6Ly9vcGVuZGF0YS5hcmNnaXMuY29tL2RhdGFzZXRzLzJlOTAzN2ZkNWJlZjQwNjQ4OGZmZTViYjY3ZDIxMzEyXzAuZ2VvanNvbiIpDQoNClBoaWxseVNlcHRhU3RvcHMgPC0gDQogIHJiaW5kKA0KICAgICBlbCAlPiUgDQogICAgICBtdXRhdGUoTGluZSA9ICJFbCIpICU+JQ0KICAgICAgZHBseXI6OnNlbGVjdChTdGF0aW9uLCBMaW5lKSwNCiAgICAgQnJvYWRfU3QgJT4lDQogICAgICBtdXRhdGUoTGluZSA9IkJyb2FkX1N0IikgJT4lDQogICAgICBkcGx5cjo6c2VsZWN0KFN0YXRpb24sIExpbmUpKSAlPiUNCiAgc3RfdHJhbnNmb3JtKHN0X2Nycyh0cmFjdHMyMSkpICANCg0KIyMgTWFwcGluZyBuZWFyZXN0IHRyYW5zaXQgc3RvcA0KDQpuZWFyZXN0X3N0YXRpb24gPC0gc2Y6OnN0X25lYXJlc3RfZmVhdHVyZShkYXRhLCBQaGlsbHlTZXB0YVN0b3BzKQ0KDQojIyBDb252ZXJ0aW5nIHRvIHJzZ2VvIGdlb21ldHJpZXMNCg0KeCA8LSByc2dlbzo6YXNfcnNnZW8oZGF0YSkNCnkgPC0gcnNnZW86OmFzX3JzZ2VvKFBoaWxseVNlcHRhU3RvcHMpDQoNCiMjIENhbGN1bGF0aW5nIGRpc3RhbmNlDQoNCmRhdGEkZGlzdF90b19uZWFyZXN0X3N0YXRpb24gPC0gcnNnZW86OmRpc3RhbmNlX2V1Y2xpZGVhbl9wYWlyd2lzZSh4LCB5W25lYXJlc3Rfc3RhdGlvbl0pDQoNCiMgTmVhcmVzdCBUcmFpbHMNCiMjIEFkZGluZyBkYXRhIG9uIFBoaWxhZGVscGhpYSBUcmFpbHMgDQoNClBoaWxseVRyYWlscyA8LQ0KIHN0X3JlYWQoIi4vZGF0YS9QUFJfVHJhaWxzLmdlb2pzb24iKSU+JQ0KICBzdF90cmFuc2Zvcm0oc3RfY3JzKHRyYWN0czIxKSkNCg0KIyMgTWFwcGluZyBuZWFyZXN0IHRyYWlsDQoNCm5lYXJlc3RfdHJhaWwgPC0gc2Y6OnN0X25lYXJlc3RfZmVhdHVyZShkYXRhLCBQaGlsbHlUcmFpbHMpDQoNCiMjIENvbnZlcnRpbmcgdG8gcnNnZW8gZ2VvbWV0cmllcw0KDQp4IDwtIHJzZ2VvOjphc19yc2dlbyhkYXRhKQ0KeSA8LSByc2dlbzo6YXNfcnNnZW8oUGhpbGx5VHJhaWxzKQ0KDQojIyBDYWxjdWxhdGluZyBkaXN0YW5jZQ0KDQpkYXRhJGRpc3RfdG9fbmVhcmVzdF90cmFpbCA8LSByc2dlbzo6ZGlzdGFuY2VfZXVjbGlkZWFuX3BhaXJ3aXNlKHgsIHlbbmVhcmVzdF90cmFpbF0pDQoNCiMgTmVhcmVzdCBUZW5uaXMgQ291cnRzDQojIyBBZGRpbmcgZGF0YSBvbiBQaGlsYWRlbHBoaWEgVGVubmlzIENvdXJ0cyANCg0KUGhpbGx5VGVubmlzQ291cnRzIDwtDQogc3RfcmVhZCgiLi9kYXRhL1BQUl9UZW5uaXNfQ291cnRzLmdlb2pzb24iKSU+JQ0KICBzdF90cmFuc2Zvcm0oc3RfY3JzKHRyYWN0czIxKSkNCg0KIyMgTWFwcGluZyBuZWFyZXN0IHRlbm5pcyBjb3VydA0KDQpuZWFyZXN0X3Rlbm5pc2NvdXJ0IDwtIHNmOjpzdF9uZWFyZXN0X2ZlYXR1cmUoZGF0YSwgUGhpbGx5VGVubmlzQ291cnRzKQ0KDQojIyBDb252ZXJ0aW5nIHRvIHJzZ2VvIGdlb21ldHJpZXMNCg0KeCA8LSByc2dlbzo6YXNfcnNnZW8oZGF0YSkNCnkgPC0gcnNnZW86OmFzX3JzZ2VvKFBoaWxseVRlbm5pc0NvdXJ0cykNCg0KIyMgQ2FsY3VsYXRpbmcgZGlzdGFuY2UNCg0KZGF0YSRkaXN0X3RvX25lYXJlc3RfdGVubmlzY291cnQgPC0gcnNnZW86OmRpc3RhbmNlX2V1Y2xpZGVhbl9wYWlyd2lzZSh4LCB5W25lYXJlc3RfdGVubmlzY291cnRdKQ0KDQojIE5lYXJlc3QgUGxheWdyb3VuZHMNCiMjIEFkZGluZyBkYXRhIG9uIFBoaWxhZGVscGhpYSBQbGF5Z3JvdW5kcyANCg0KUGhpbGx5UGxheWdyb3VuZHMgPC0NCiBzdF9yZWFkKCIuL2RhdGEvUFBSX1BsYXlncm91bmRzLmdlb2pzb24iKSU+JQ0KICBzdF90cmFuc2Zvcm0oc3RfY3JzKHRyYWN0czIxKSkNCg0KIyMgTWFwcGluZyBuZWFyZXN0IHBsYXlncm91bmQNCg0KbmVhcmVzdF9wbGF5Z3JvdW5kIDwtIHNmOjpzdF9uZWFyZXN0X2ZlYXR1cmUoZGF0YSwgUGhpbGx5UGxheWdyb3VuZHMpDQoNCiMjIENvbnZlcnRpbmcgdG8gcnNnZW8gZ2VvbWV0cmllcw0KDQp4IDwtIHJzZ2VvOjphc19yc2dlbyhkYXRhKQ0KeSA8LSByc2dlbzo6YXNfcnNnZW8oUGhpbGx5UGxheWdyb3VuZHMpDQoNCiMjIENhbGN1bGF0aW5nIGRpc3RhbmNlDQoNCmRhdGEkZGlzdF90b19uZWFyZXN0X3BsYXlncm91bmQgPC0gcnNnZW86OmRpc3RhbmNlX2V1Y2xpZGVhbl9wYWlyd2lzZSh4LCB5W25lYXJlc3RfcGxheWdyb3VuZF0pDQoNCiMgTmVhcmVzdCBQb29scw0KIyMgQWRkaW5nIGRhdGEgb24gUGhpbGFkZWxwaGlhIFBvb2xzIA0KDQpQaGlsbHlQb29scyA8LQ0KIHN0X3JlYWQoIi4vZGF0YS9QUFJfU3dpbW1pbmdfUG9vbHMuZ2VvanNvbiIpJT4lDQogIHN0X3RyYW5zZm9ybShzdF9jcnModHJhY3RzMjEpKQ0KDQojIyBNYXBwaW5nIG5lYXJlc3QgcG9vbA0KDQpuZWFyZXN0X3Bvb2wgPC0gc2Y6OnN0X25lYXJlc3RfZmVhdHVyZShkYXRhLCBQaGlsbHlQb29scykNCg0KIyMgQ29udmVydGluZyB0byByc2dlbyBnZW9tZXRyaWVzDQoNCnggPC0gcnNnZW86OmFzX3JzZ2VvKGRhdGEpDQp5IDwtIHJzZ2VvOjphc19yc2dlbyhQaGlsbHlQb29scykNCg0KIyMgQ2FsY3VsYXRpbmcgZGlzdGFuY2UNCg0KZGF0YSRkaXN0X3RvX25lYXJlc3RfcG9vbCA8LSByc2dlbzo6ZGlzdGFuY2VfZXVjbGlkZWFuX3BhaXJ3aXNlKHgsIHlbbmVhcmVzdF9wb29sXSkNCg0KIyBOZWFyZXN0IFBvbGljZSBTdGF0aW9ucw0KIyMgQWRkaW5nIGRhdGEgb24gUGhpbGFkZWxwaGlhIFBvbGljZSBTdGF0aW9ucw0KDQpQaGlsbHlQb2xpY2VTdGF0aW9ucyA8LQ0KIHN0X3JlYWQoIi4vZGF0YS9Qb2xpY2VfU3RhdGlvbnMuZ2VvanNvbiIpJT4lDQogIHN0X3RyYW5zZm9ybShzdF9jcnModHJhY3RzMjEpKQ0KDQojIyBNYXBwaW5nIG5lYXJlc3QgcG9saWNlIHN0YXRpb24NCg0KbmVhcmVzdF9wb2xpY2Vfc3RhdGlvbiA8LSBzZjo6c3RfbmVhcmVzdF9mZWF0dXJlKGRhdGEsIFBoaWxseVBvbGljZVN0YXRpb25zKQ0KDQojIyBDb252ZXJ0aW5nIHRvIHJzZ2VvIGdlb21ldHJpZXMNCg0KeCA8LSByc2dlbzo6YXNfcnNnZW8oZGF0YSkNCnkgPC0gcnNnZW86OmFzX3JzZ2VvKFBoaWxseVBvbGljZVN0YXRpb25zKQ0KDQojIyBDYWxjdWxhdGluZyBkaXN0YW5jZQ0KDQpkYXRhJGRpc3RfdG9fbmVhcmVzdF9wb2xpY2Vfc3RhdGlvbiA8LSByc2dlbzo6ZGlzdGFuY2VfZXVjbGlkZWFuX3BhaXJ3aXNlKHgsIHlbbmVhcmVzdF9wb2xpY2Vfc3RhdGlvbl0pDQoNCmBgYA0KDQpBIGRpc3RhbmNlIGFuYWx5c2lzIGlzIHBlcmZvcm1lZCBmb3IgZWFjaCBvZiB0aGUgYWJvdmUgYW1lbml0aWVzIGluIHJlbGF0aW9uIHRvIG91ciBob21lIHZhbHVlIGRhdGFzZXQgdG8gaWRlbnRpZnkgdGhlIG5lYXJlc3QgYW1lbml0eSBvZiBlYWNoIGtpbmQgdG8gZWFjaCBob3VzZSBpbiB0aGUgb3JpZ2luYWwgZGF0YXNldC4gVGhpcyBhbmFseXNpcyBpcyBjb25kdWN0ZWQgYXMgYSBwcmVwYXJhdG9yeSBzdGVwLCB0aGUgZGlzdGFuY2VzIGRlcml2ZWQgZnJvbSB0aGlzIGFuYWx5c2lzIGFyZSBzdHVkaWVkIG5leHQgdG8gZGV0ZXJtaW5lIGlmIHRoZXNlIHNwYXRpYWwgZmFjdG9ycyBwbGF5IGEgcm9sZSBpbiBpbmZsdWVuY2luZyBob21lIHByaWNlcy4NCg0KIyMjIEsgTmVhcmVzdCBOZWlnaGJvcnMgDQoNCkluIG9yZGVyIHRvIGV2YWx1YXRlIHRoZSBpbnN0YW5jZXMgb2YgZ3VuIHZpb2xlbmNlIGFyb3VuZCBhIGxpc3RlZCBob21lLCB0aGUgbWV0aG9kIG9mIGstTmVhcmVzdCBOZWlnaGJvcnMgQW5hbHlzaXMgaXMgdXNlZC4gSW4gdGhpcyBjYXNlLCBhIGhpZ2hlciB2YWx1ZSBvZiBrPTUgaXMgcHJlZmVycmVkIHRvIGFjY291bnQgZm9yIHRoZSBzcGF0aWFsIGRpc3RyaWJ1dGlvbiBvZiB0aGUgaW5zdGFuY2VzIGluIHJlbGF0aW9uIHRvIHRoZSBob21lcyBhbmQgdG8gZW5zdXJlIHRoYXQgdGhlcmUgYXJlIGVub3VnaCBuZWlnaGJvciBvYnNlcnZhdGlvbnMgZm9yIGVhY2ggbGlzdGVkIGhvbWUgZm9yIGJldHRlciBiYWxhbmNlIGFjcm9zcyB0aGUgZGF0YXNldC4gQSBoaWdoZXIgayB2YWx1ZSBpcyBhbHNvIHByZWZlcnJlZCBmb3IgdGhlIHJvYnVzdG5lc3MgYW5kIGhpZ2hlciBnZW5lcmFsaXphYmlsaXR5IGl0IG9mZmVycyBkdXJpbmcgcHJlZGljdGlvbi4NCg0KYGBge3J9DQojIFBoaWxhZGVscGhpYSBTaG9vdGluZ3MNCiMjIEFkZGluZyBkYXRhIG9uIFBoaWxhZGVscGhpYSBTaG9vdGluZ3MNCg0KUGhpbGx5U2hvb3RpbmdzIDwtDQogc3RfcmVhZCgiLi9kYXRhL3Nob290aW5nczIwMjEuZ2VvanNvbiIpJT4lDQogIGRwbHlyOjpzZWxlY3QoImRhdGVfIiwgInBvaW50X3giLCAicG9pbnRfeSIsICJnZW9tZXRyeSIpJT4lDQogIHN0X3RyYW5zZm9ybShzdF9jcnMoZGF0YSkpJT4lDQogIG5hLm9taXQoKSANCg0KIyNjYWxjdWxhdGluZyBkaXN0YW5jZSB0byBuZWFyZXN0IG5laWdoYm9ycw0KDQpsaWJyYXJ5KHNmKQ0KbGlicmFyeShzcGF0c3RhdCkNCmxpYnJhcnkoY2xhc3MpDQoNCmRhdGFfcHBwIDwtIGFzLnBwcChkYXRhKQ0KUGhpbGx5U2hvb3RpbmdzX3BwcCA8LSBhcy5wcHAoUGhpbGx5U2hvb3RpbmdzKQ0KDQpkYXRhJGNyaW1lX25uNSA8LSBubmNyb3NzKGRhdGFfcHBwLCBQaGlsbHlTaG9vdGluZ3NfcHBwLCBrID0gNSkNCg0KYGBgDQoNCmBgYHtyIFByaWNlL1NxZnQsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0KDQp0cmFjdHMyMSA8LSANCiAgdHJhY3RzMjEgJT4lDQogIG11dGF0ZShQY3RXaGl0ZSA9ICgocmFjZV93aGl0ZS90b3RhbHBvcCkqMTAwKSwNCiAgICAgICAgIFBjdEJsYWNrID0gKChyYWNlX2JsYWNrL3RvdGFscG9wKSoxMDApLA0KICAgICAgICAgUGN0SGlzcGFuaWMgPSAoKGhpc3BhbmljX2xhdGluby90b3RhbHBvcCkqMTAwKSwNCiAgICAgICAgIFBjdEJhY2hlbG9ycyA9ICgoZWR1X2JhY2hlbG9ycy90b3RhbHBvcCkqMTAwKSwNCiAgICAgICAgIFBjdFBvdmVydHkgPSAoKHBvcF9iZWxvd19wb3ZlcnR5L3RvdGFscG9wKSoxMDApKQ0KDQpgYGANCg0KDQojIyBKb2luaW5nIERhdGEgDQoNCkluIG9yZGVyIHRvIGJlZ2luIGNvbmR1Y3RpbmcgZXhwbG9yYXRvcnkgYW5hbHlzZXMgb2YgdGhlIHJlbGF0aW9ucyB3aXRoaW4gdGhlIGRhdGFzZXQsIHRoZSBmb3VuZGF0aW9uYWwgZGF0YXNldCBpcyBqb2luZWQgd2l0aCB0aGUgZGF0YXNldHMgY29udGFpbmluZyByZWxldmFudCBzcGF0aWFsIGFuZCBkZW1vZ3JhcGhpYyBhdHRyaWJ1dGVzLg0KDQoNCmBgYHtyIGNlbnN1cyBqb2lufQ0KDQojIGpvaW5pbmcgY2Vuc3VzIGRhdGENCg0KZGF0YSA8LSANCiAgc3Rfam9pbihkYXRhLCB0cmFjdHMyMSkNCg0KYGBgDQoNCmBgYHtyIHNhdmUgZm9yIHByZWRpY3Rpb259DQoNCiNzYXZpbmcgZm9yIHByZWRpY3Rpb24gDQoNCmRhdGFfb2cgPC0gZGF0YQ0KDQpkYXRhIDwtIGRhdGEgJT4lDQogIGZpbHRlcihzYWxlX3ByaWNlID4gMCkgDQoNCmBgYA0KDQoNCiMgRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcw0KDQpQcm92aWRlZCBiZWxvdyBhcmUgdGhlIHN1bW1hcnkgc3RhdGlzdGljcyBmb3IgdGhlIGRpZmZlcmVudCB0eXBlcyBvZiB2YXJpYWJsZXMgY29uc2lkZXJlZCAoaW50ZXJuYWwsIHNwYXRpYWwsIGFuZCBkZW1vZ3JhcGhpYykuIFRoZSBleHBsb3JhdGlvbiBmdXJ0aGVyIGxvb2tzIGF0IHRoZSBkaXN0cmlidXRpb24gb2Ygbm9uLW51bWVyaWMgY2F0ZWdvcmljYWwgdmFyaWFibGVzIHRocm91Z2ggYmFyIHBsb3RzLiBJdCBmaW5hbGx5IGluc3BlY3RzIHRoZSBkZXBlbmRlbmNlIG9mICdob21lIHNhbGUgcHJpY2UnIG9uIG90aGVyIGNvbnRpbnVvdXMgaW5kZXBlbmRlbnQgdmFyaWFibGVzIGluY2x1ZGluZyB0b3RhbCBhcmVhLCBtZWRpYW4gaW5jb21lLCBhbmQgcmFjaWFsIGRpc3RyaWJ1dGlvbiBhbW9uZyBvdGhlcnMuIA0KDQojIyBTdW1tYXJ5IFN0YXRpc3RpY3M6IEludGVybmFsIFZhcmlhYmxlcw0KDQpUaGVzZSBzdGF0aXN0aWNzIHByb3ZpZGUgYW4gb3ZlcnZpZXcgb2YgdGhlIGNlbnRyYWwgdGVuZGVuY3ksIHNwcmVhZCwgYW5kIHJhbmdlIG9mIHRoZSBpbnRlcm5hbCB2YXJpYWJsZXMgZm9yIFBoaWxhZGVscGhpYSBob21lcywgd2hpY2ggaW5jbHVkZSBzYWxlIHByaWNlcywgcHJpY2UgcGVyIHNxdWFyZSBmb290LCBsaXZpbmcgYXJlYSBzaXplLCB5ZWFyIGJ1aWx0LCBudW1iZXIgb2Ygcm9vbXMsIG51bWJlciBvZiBiYXRocm9vbXMsIGFuZCBudW1iZXIgb2YgYmVkcm9vbXMuDQoNCg0KYGBge3Igc3VtbSBzdGF0IGludH0NCkludGVybmFsVmFyaWFibGVzIDwtIGRhdGEgDQoNCkludGVybmFsVmFyaWFibGVzIDwtIHN0X2Ryb3BfZ2VvbWV0cnkoSW50ZXJuYWxWYXJpYWJsZXMpDQoNCkludGVybmFsVmFyaWFibGVzIDwtIEludGVybmFsVmFyaWFibGVzICU+JQ0KICBkcGx5cjo6c2VsZWN0KCJzYWxlX3ByaWNlIiwgIlByaWNlUGVyU3FmdCIsICJ0b3RhbF9saXZhYmxlX2FyZWEiLCAieWVhcl9idWlsdCIsICJudW1iZXJfb2Zfcm9vbXMiLCAibnVtYmVyX29mX2JhdGhyb29tcyIsICJudW1iZXJfb2ZfYmVkcm9vbXMiKSANCg0Kc3RhcmdhemVyKGFzLmRhdGEuZnJhbWUoSW50ZXJuYWxWYXJpYWJsZXMpLCB0eXBlPSJ0ZXh0IiwgZGlnaXRzPTEsIHRpdGxlID0gIkRlc2NyaXB0aXZlIFN0YXRpc3RpY3MgZm9yIFBoaWxhZGVscGhpYSBIb21lcyBJbnRlcm5hbCBWYXJpYWJsZXMgKEZpZ3VyZSA0LjEpIiwgb3V0ID0gIlRyYWluaW5nX1BITEludGVybmFsLnR4dCIpDQpgYGANCg0KSXQgaXMgb2JzZXJ2ZWQgdGhhdCB0aGUgYXZlcmFnZSBzYWxlIHByaWNlIG9mIGhvbWVzIGluIFBoaWxhZGVscGhpYSBpcyBhcHByb3hpbWF0ZWx5IDI4Miw4NTcuOCBVU0QuIFRoZSBzYWxlIHByaWNlcyB2YXJ5IHF1aXRlIHdpZGVseSB3aXRoIGEgaGlnaCBzdGFuZGFyZCBkZXZpYXRpb24sIGluZGljYXRpbmcgc2lnbmlmaWNhbnQgZGlzcGVyc2lvbiBpbiBwcmljZXMuIEl0IGlzIGFsc28gb2JzZXJ2ZWQgdGhhdCB0aGUgYXZlcmFnZSBsaXZhYmxlIGFyZWEgaW4gUGhpbGFkZWxwaGlhIGlzIGFwcHJveGltYXRlbHkgMTMzNCBzcWZ0IHdpdGggdGhlIHByaWNlIHBlciBzcXVhcmUgZm9vdCBmb3IgaG9tZXMgcG9zaXRpb25lZCBhdCBhYm91dCAyMDkuMyBVU0QuIA0KDQpUaGUgYXZlcmFnZSBhZ2UgZm9yIGEgaG91c2UgaW4gUGhpbGFkZWxwaGlhIGlzIGFwcHJveGltYXRlbHkgODUgeWVhcnMgaW5kaWNhdGluZyBhIGxhcmdlIHNoYXJlIG9mIHRoZSBtYXJrZXQgaXMgaGVsZCBieSBob21lcyBjb25zdHJ1Y3RlZCBvdmVyIGEgY2VudHVyeSBhZ28uIA0KDQpGaW5hbGx5LCBob21lcyBpbiBQaGlsYWRlbHBoaWEgb24gYXZlcmFnZSBzZWVtIHRvIGhhdmUgNiByb29tcyBvdmVyYWxsIHdpdGggYXBwcm94aW1hdGVseSAzIGJlZHJvb21zIGFuZCAxIGJhdGhyb29tLiANCg0KIyMgU3VtbWFyeSBTdGF0aXN0aWNzOiBEZW1vZ3JhcGhpYyBWYXJpYWJsZXMNCg0KVGhlc2Ugc3RhdGlzdGljcyBwcm92aWRlIGFuIG92ZXJ2aWV3IG9mIHRoZSBjZW50cmFsIHRlbmRlbmN5LCBzcHJlYWQsIGFuZCByYW5nZSBvZiB0aGUgaW50ZXJuYWwgdmFyaWFibGVzIGZvciBQaGlsYWRlbHBoaWEgaG9tZXMsIHdoaWNoIGluY2x1ZGUgbWVkaWFuIGluY29tZSwgcGVyY2VudGFnZSBvZiB3aGl0ZSBwb3B1bGF0aW9uLCBwZXJjZW50YWdlIG9mIGJsYWNrIHBvcHVsYXRpb24sIHBlcmNlbnRhZ2Ugb2YgaGlzcGFuaWMgcG9wdWxhdGlvbiwgcGVyY2VudGFnZSBvZiBwb3B1bGF0aW9uIHdpdGggYSBiYWNoZWxvcidzIGRlZ3JlZSBhbmQgcGVyY2VudGFnZSBvZiBwb3B1bGF0aW9uIGxpdmluZyBpbiBwb3ZlcnR5Lg0KDQoNCmBgYHtyIHN1bW0gc3RhdCBkZW19DQpEZW1vZ3JhcGhpY1ZhcmlhYmxlcyA8LSBkYXRhIA0KDQpEZW1vZ3JhcGhpY1ZhcmlhYmxlcyA8LSBzdF9kcm9wX2dlb21ldHJ5KERlbW9ncmFwaGljVmFyaWFibGVzKQ0KDQpEZW1vZ3JhcGhpY1ZhcmlhYmxlcyA8LSBEZW1vZ3JhcGhpY1ZhcmlhYmxlcyAlPiUNCiAgZHBseXI6OnNlbGVjdCgiUGN0V2hpdGUiLCAiUGN0QmxhY2siLCAiUGN0SGlzcGFuaWMiLCAiUGN0QmFjaGVsb3JzIiwgIlBjdFBvdmVydHkiLCAibWVkX2luY29tZSIpIA0KDQpzdGFyZ2F6ZXIoYXMuZGF0YS5mcmFtZShEZW1vZ3JhcGhpY1ZhcmlhYmxlcyksIHR5cGU9InRleHQiLCBkaWdpdHM9MSwgdGl0bGUgPSAiRGVzY3JpcHRpdmUgU3RhdGlzdGljcyBmb3IgUGhpbGFkZWxwaGlhIEhvbWVzIERlbW9ncmFwaGljIFZhcmlhYmxlcyAoRmlndXJlIDQuMSkiLCBvdXQgPSAiVHJhaW5pbmdfUEhMU3BhdGlhbC50eHQiKQ0KYGBgDQoNClRoZSB0YWJsZSBwcmVzZW50cyBkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzIGZvciBkZW1vZ3JhcGhpYyB2YXJpYWJsZXMgcmVsYXRlZCB0byBQaGlsYWRlbHBoaWEgaG9tZXMsIG9mZmVyaW5nIGluc2lnaHRzIGludG8gdGhlIGNoYXJhY3RlcmlzdGljcyBvZiB0aGUgcG9wdWxhdGlvbiBpbiBkaWZmZXJlbnQgYXJlYXMuDQoNCk9uIGF2ZXJhZ2UsIGFwcHJveGltYXRlbHkgNDQuMSUgb2YgdGhlIHBvcHVsYXRpb24gaW4gdGhlIG9ic2VydmVkIGFyZWFzIGlzIFdoaXRlLiBUaGUgcmFuZ2Ugb2YgV2hpdGUgcG9wdWxhdGlvbiBwZXJjZW50YWdlcyBzcGFucyBmcm9tIDAuMCUgdG8gOTUuNyUsIGluZGljYXRpbmcgYSB3aWRlIGRpdmVyc2l0eSBpbiByYWNpYWwgY29tcG9zaXRpb24gYWNyb3NzIGRpZmZlcmVudCBhcmVhcyBvZiBQaGlsYWRlbHBoaWEuIFRoZSBzYW1lIGhvbGRzIHRydWUgZm9yIEJsYWNrIGFuZCBIaXNwYW5pYyBwb3B1bGF0aW9uIHBlcmNlbnRhZ2VzIGluZGljYXRpbmcgdGhlIHByZXNlbmNlIG9mIFdoaXRlLW1ham9yaXR5LCBCbGFjayBtYWpvcml0eSBhbmQgSGlzcGFuaWMgbWFqb3JpdHkgbmVpZ2hib3Job29kcyBpbiBkaWZmZXJlbnQgcGFydHMgb2YgUGhpbGFkZWxwaGlhLiBPbiBhdmVyYWdlLCBhYm91dCAxNC4xJSBvZiB0aGUgcG9wdWxhdGlvbiBpbiB0aGUgb2JzZXJ2ZWQgYXJlYXMgaG9sZHMgYSBCYWNoZWxvcidzIGRlZ3JlZSBvciBoaWdoZXIuDQoNClRoZSBhdmVyYWdlIG1lZGlhbiBob3VzZWhvbGQgaW5jb21lIGluIHRoZSBvYnNlcnZlZCBhcmVhcyBpcyAkNjEsMzIwLjAuIEhvd2V2ZXIsIGFwcHJveGltYXRlbHkgMjAuNCUgb2YgdGhlIHBvcHVsYXRpb24gaW4gdGhlIG9ic2VydmVkIGFyZWFzIGZhbGxzIGJlbG93IHRoZSBwb3ZlcnR5IGxldmVsLCBvbiBhdmVyYWdlLg0KDQojIyBTdW1tYXJ5IFN0YXRpc3RpY3M6IFNwYXRpYWwgVmFyaWFibGVzDQoNClRoZXNlIHN0YXRpc3RpY3MgcHJvdmlkZSBhbiBvdmVydmlldyBvZiB0aGUgY2VudHJhbCB0ZW5kZW5jeSwgc3ByZWFkLCBhbmQgcmFuZ2Ugb2YgdGhlIGludGVybmFsIHZhcmlhYmxlcyBmb3IgUGhpbGFkZWxwaGlhIGhvbWVzLCB3aGljaCBpbmNsdWRlIGRpc3RhbmNlcyB0byBrZXkgbmVhcmVzdCBhbWVuaXRpZXMgYW5kIHRocmVhdGVuaW5nIGFjdGl2aXRpZXMgaW5jbHVkaW5nIG5lYXJlc3Qgc2Nob29scywgbmVhcmVzdCBwcml2YXRlIHNjaG9vbHMsIG5lYXJlc3QgbGFuZG1hcmtzLCBuZWFyZXN0IGNvbW1lcmNpYWwgY29ycmlkb3JzLCBuZWFyZXN0IHRyYWlscywgbmVhcmVzdCBwb2xpY2Ugc3RhdGlvbnMsIGFuZCBuZWFyZXN0IGNyaW1lIGluc3RhbmNlcy4NCg0KDQpgYGB7ciBzdW1tIHN0YXQgc3BhdH0NCg0KU3BhdGlhbFZhcmlhYmxlcyA8LSBkYXRhIA0KDQpTcGF0aWFsVmFyaWFibGVzIDwtIHN0X2Ryb3BfZ2VvbWV0cnkoU3BhdGlhbFZhcmlhYmxlcykNCg0KU3BhdGlhbFZhcmlhYmxlcyA8LSBTcGF0aWFsVmFyaWFibGVzICU+JQ0KICBkcGx5cjo6c2VsZWN0KCJkaXN0X3RvX25lYXJlc3Rfc2Nob29sIiwgImRpc3RfdG9fbmVhcmVzdF9wdnRfc2Nob29sIiwgImRpc3RfdG9fbmVhcmVzdF9sYW5kbWFyayIsICJkaXN0X3RvX2NvbW1fY29yciIsICJkaXN0X3RvX25lYXJlc3Rfc3RhdGlvbiIsICJkaXN0X3RvX25lYXJlc3RfdHJhaWwiLCAiZGlzdF90b19uZWFyZXN0X3BvbGljZV9zdGF0aW9uIiwiY3JpbWVfbm41IikgDQoNCnN0YXJnYXplcihhcy5kYXRhLmZyYW1lKFNwYXRpYWxWYXJpYWJsZXMpLCB0eXBlPSJ0ZXh0IiwgZGlnaXRzPTEsIHRpdGxlID0gIkRlc2NyaXB0aXZlIFN0YXRpc3RpY3MgZm9yIFBoaWxhZGVscGhpYSBIb21lcyBTcGF0aWFsIFZhcmlhYmxlcyAoRmlndXJlIDQuMSkiLCBvdXQgPSAiVHJhaW5pbmdfUEhMU3BhdGlhbC50eHQiKQ0KYGBgDQpUaGVzZSBzdGF0aXN0aWNzIG9mZmVyIGluc2lnaHRzIGludG8gdGhlIHNwYXRpYWwgY2hhcmFjdGVyaXN0aWNzIG9mIFBoaWxhZGVscGhpYSBuZWlnaGJvcmhvb2RzIHdoZXJlIHRoZSAiTWVhbiIgaW4gZWFjaCBjYXNlIHJlcHJlc2VudHMgdGhlIGF2ZXJhZ2UgZGlzdGFuY2UgdG8gYSBzcGVjaWZpYyBhbWVuaXR5IGZyb20gYSBsaXN0ZWQgcHJvcGVydHkuIA0KDQojIyBQbG90dGluZyBDYXRlZ29yaWNhbCBWYXJpYWJsZXMgDQoNClRoZSBmb2xsb3dpbmcgZXhwbG9yYXRvcnkgYW5hbHlzaXMgcGxvdCwgdGl0bGVkICJQcmljZSBhcyBhIGZ1bmN0aW9uIG9mIENhdGVnb3JpY2FsIFZhcmlhYmxlcywiIGlzIGEgc2V0IG9mIGdyb3VwZWQgYmFyIGNoYXJ0cy4gRWFjaCBjaGFydCBjb3JyZXNwb25kcyB0byBhIHNlbGVjdGVkIGNhdGVnb3JpY2FsIHZhcmlhYmxlLCBhbmQgd2l0aGluIGVhY2ggY2hhcnQsIGRpZmZlcmVudCBjYXRlZ29yaWVzIG9mIHRoYXQgdmFyaWFibGUgYXJlIGNvbXBhcmVkIGJhc2VkIG9uIHRoZWlyIGluZmx1ZW5jZSBvbiB0aGUgc2FsZSBwcmljZSBvZiBob21lcy4gVGhpcyB2aXN1YWxpemF0aW9uIGhlbHBzIGFzc2VzcyBob3cgZWFjaCBjYXRlZ29yaWNhbCB2YXJpYWJsZSByZWxhdGVzIHRvIGhvbWUgc2FsZSBwcmljZXMgYW5kIGFsbG93cyBmb3IgdmlzdWFsIGNvbXBhcmlzb24gYWNyb3NzIGNhdGVnb3JpZXMuIFRoaXMgYWxsb3dzIGEgY2xlYXIgY29tcGFyaXNvbiBvZiBtdWx0aXBsZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgYXQgb25jZSwgbWFraW5nIGl0IGVhc2llciB0byBpZGVudGlmeSB0cmVuZHMgYW5kIHBhdHRlcm5zIGluIHRoZSBkYXRhLg0KDQpgYGB7ciBjYXQgcGxvdCwgZmlnLmhlaWdodD0yMCwgZmlnLndpZHRoPTE0fQ0KQ2F0ZWdvcmljYWxWYXJpYWJsZXMgPC0gZGF0YSANCkNhdGVnb3JpY2FsVmFyaWFibGVzIDwtIHN0X2Ryb3BfZ2VvbWV0cnkoQ2F0ZWdvcmljYWxWYXJpYWJsZXMpDQpDYXRlZ29yaWNhbFZhcmlhYmxlcyA8LSBDYXRlZ29yaWNhbFZhcmlhYmxlcyAlPiUNCiAgZHBseXI6OnNlbGVjdCgiQmFzZW1lbnRUeXBlIiwgIkdhcmFnZVR5cGUiLCAiVmlld1R5cGUiLCAiRXh0ZXJpb3JDb25kaXRpb25UeXBlIiwgIkludGVyaW9yQ29uZGl0aW9uVHlwZSIsICJidWlsZGluZ19jb2RlX2Rlc2NyaXB0aW9uX25ldyIsInNhbGVfcHJpY2UiKSANCg0KQ2F0ZWdvcmljYWxWYXJpYWJsZXMgJT4lDQogIGdhdGhlcihDYXRlZ29yaWNhbFZhcmlhYmxlcywgVmFsdWUsIC1zYWxlX3ByaWNlKSAlPiUNCiAgZ2dwbG90KGFlcyhWYWx1ZSwgc2FsZV9wcmljZSkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikgKw0KICBmYWNldF93cmFwKH5DYXRlZ29yaWNhbFZhcmlhYmxlcywgc2NhbGVzID0gImZyZWUiLCBuY29sPTIpICsNCiAgbGFicyh0aXRsZSA9ICJQcmljZSBhcyBhIGZ1bmN0aW9uIG9mIENhdGVnb3JpY2FsIFZhcmlhYmxlcyIpICsNCiAgcGxvdFRoZW1lKCkgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQ0KDQpgYGANCg0KDQojIEV4cGxvcmF0b3J5IE1hcHBpbmcgYW5kIEFuYWx5c2lzIA0KDQojIyBNYXBwaW5nIEludGVybmFsIFZhcmlhYmxlcw0KDQpgYGB7ciBtYXAgaW50fQ0KDQojI01hcHBpbmcgSW50ZXJuYWwgVmFyaWFibGVzDQojIE1hcHBpbmcgc2FsZSBwcmljZQ0KDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IHRyYWN0czIxLCBmaWxsID0gImdyZXk4OSIsIGNvbG9yID0gImRhcmtncmV5IikgKw0KICBnZW9tX3NmKGRhdGEgPSBkYXRhLCBhZXMoY29sb3VyID0gcTUoc2FsZV9wcmljZSkpLCANCiAgICAgICAgICBzaG93LmxlZ2VuZCA9ICJwb2ludCIsIHNpemUgPSAuNzUpICsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlZSwNCiAgICAgICAgICAgICAgICAgICBsYWJlbHM9cUJyKGRhdGEsICJzYWxlX3ByaWNlIiksDQogICAgICAgICAgICAgICAgICAgbmFtZT0iUXVpbnRpbGUgQnJlYWtzOlxuIFNhbGUgUHJpY2UiLCANCiAgICAgICAgICAgICAgICAgICBuYS52YWx1ZSA9IE5BKSArDQogIGxhYnModGl0bGU9IlByb3BlcnRpZXMgYnkgU2FsZSBQcmljZSIsIHN1YnRpdGxlID0gIlBoaWxhZGVscGhpYSAyMDIyLTIwMjMiLCANCiAgICAgIGNhcHRpb249IkZpZ3VyZSAzLjEuMSIpICsNCiAgbWFwVGhlbWUoKQ0KDQojIE1hcHBpbmcgU2l6ZQ0KDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IHRyYWN0czIxLCBmaWxsID0gImdyZXk4OSIsIGNvbG9yID0gImRhcmtncmV5IikgKw0KICBnZW9tX3NmKGRhdGEgPSBkYXRhLCBhZXMoY29sb3VyID0gcTUodG90YWxfbGl2YWJsZV9hcmVhKSksIA0KICAgICAgICAgIHNob3cubGVnZW5kID0gInBvaW50Iiwgc2l6ZSA9IC43NSkgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGVlLA0KICAgICAgICAgICAgICAgICAgIGxhYmVscz1xQnIoZGF0YSwgInRvdGFsX2xpdmFibGVfYXJlYSIpLA0KICAgICAgICAgICAgICAgICAgIG5hbWU9IlF1aW50aWxlIEJyZWFrczpcbkFyZWEgaW4gU3EgRnQiLCANCiAgICAgICAgICAgICAgICAgICBuYS52YWx1ZSA9IE5BKSArDQogIGxhYnModGl0bGU9IlByb3BlcnRpZXMgYnkgU2l6ZSIsIHN1YnRpdGxlID0gIlBoaWxhZGVscGhpYSAyMDIyLTIwMjMiLCANCiAgICAgIGNhcHRpb249IkZpZ3VyZSAzLjEuMiIpICsNCiAgbWFwVGhlbWUoKQ0KDQojIE1hcHBpbmcgSW50ZXJpb3IgQ29uZGl0aW9uDQoNCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhID0gdHJhY3RzMjEsIGZpbGwgPSAiZ3JleTg5IiwgY29sb3IgPSAiZGFya2dyZXkiKSArDQogIGdlb21fc2YoZGF0YSA9IGRhdGEsIGFlcyhjb2xvdXIgPSAoSW50ZXJpb3JDb25kaXRpb25UeXBlKSksIA0KICAgICAgICAgIHNob3cubGVnZW5kID0gInBvaW50Iiwgc2l6ZSA9IC41KSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZWUsIG5hbWU9IkludGVyaW9yIENvbmRpdGlvbiIsDQogICAgICAgICAgICAgICAgICAgbmEudmFsdWUgPSBOQSkgKw0KICBsYWJzKHRpdGxlPSJQcm9wZXJ0aWVzIGJ5IEludGVybmFsIENvbmRpdGlvbiIsIHN1YnRpdGxlID0gIlBoaWxhZGVscGhpYSAyMDIyLTIwMjMiLCANCiAgICAgIGNhcHRpb249IkZpZ3VyZSAzLjEuMyIpICsNCiAgbWFwVGhlbWUoKQ0KDQpgYGANCg0KDQojIyBNYXBwaW5nIFNwYXRpYWwgVmFyaWFibGVzDQoNCmBgYHtyIG1hcCBzcGF0fQ0KIyMgTWFwcGluZyBTcGF0aWFsIFZhcmlhYmxlcw0KDQojIE1hcHBpbmcgcHJvcGVydGllcyBhcm91bmQgc2Nob29scw0KDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IHRyYWN0czIxLCBmaWxsID0gImdyZXk4OSIsIGNvbG9yID0gImRhcmtncmV5IikgKw0KICBnZW9tX3NmKGRhdGEgPSBkYXRhLCBhZXMoY29sb3VyID0gcTUoc2FsZV9wcmljZSkpLCANCiAgICAgICAgICBzaG93LmxlZ2VuZCA9ICJwb2ludCIsIHNpemUgPSAuNzUsIGFscGhhPTAuMykgKyANCiAgZ2VvbV9zZihkYXRhID0gUGhpbGx5U2Nob29scywgY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDEuNSwgYWxwaGE9MC42KSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZWUsDQogICAgICAgICAgICAgICAgICAgbGFiZWxzPXFCcihkYXRhLCAic2FsZV9wcmljZSIpLA0KICAgICAgICAgICAgICAgICAgIG5hbWU9IlF1aW50aWxlIEJyZWFrczpcblNhbGUgUHJpY2VzIiwgDQogICAgICAgICAgICAgICAgICAgbmEudmFsdWUgPSBOQSkgKyANCiAgbGFicyh0aXRsZT0iUHJvcGVydGllcyBBcm91bmQgU2Nob29scyIsIHN1YnRpdGxlID0gIlBoaWxhZGVscGhpYSAyMDIyLTIwMjMiLCANCiAgICAgIGNhcHRpb249IkZpZ3VyZSAzLjIuMSIpICsNCiAgbWFwVGhlbWUoKQ0KDQojIE1hcHBpbmcgcHJvcGVydGllcyBhcm91bmQgY29tbWVyY2lhbCBjb3JyaWRvcnMNCg0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGEgPSB0cmFjdHMyMSwgZmlsbCA9ICJncmV5ODkiLCBjb2xvciA9ICJkYXJrZ3JleSIpICsNCiAgZ2VvbV9zZihkYXRhID0gZGF0YSwgYWVzKGNvbG91ciA9IHE1KHNhbGVfcHJpY2UpKSwgDQogICAgICAgICAgc2hvdy5sZWdlbmQgPSAicG9pbnQiLCBzaXplID0gLjc1LCBhbHBoYT0wLjMpICsgDQogIGdlb21fc2YoZGF0YSA9IFBoaWxseUNvbUNvcnIsIGNvbG91ciA9ICJibGFjayIsIGZpbGw9ImJsYWNrIiwgYWxwaGE9MC42KSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZWUsDQogICAgICAgICAgICAgICAgICAgbGFiZWxzPXFCcihkYXRhLCAic2FsZV9wcmljZSIpLA0KICAgICAgICAgICAgICAgICAgIG5hbWU9IlF1aW50aWxlIEJyZWFrczpcblNhbGUgUHJpY2VzIiwgDQogICAgICAgICAgICAgICAgICAgbmEudmFsdWUgPSBOQSkgKyANCiAgbGFicyh0aXRsZT0iUHJvcGVydGllcyBBcm91bmQgQ29tbWVyY2lhbCBDb3JyaWRvcnMiLCBzdWJ0aXRsZSA9ICJQaGlsYWRlbHBoaWEgMjAyMi0yMDIzIiwgDQogICAgICBjYXB0aW9uPSJGaWd1cmUgMy4yLjIiKSArDQogIG1hcFRoZW1lKCkNCg0KIyBNYXBwaW5nIHByb3BlcnRpZXMgYXJvdW5kIGxhbmRtYXJrcw0KDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IHRyYWN0czIxLCBmaWxsID0gImdyZXk4OSIsIGNvbG9yID0gImRhcmtncmV5IikgKw0KICBnZW9tX3NmKGRhdGEgPSBkYXRhLCBhZXMoY29sb3VyID0gcTUoc2FsZV9wcmljZSkpLCANCiAgICAgICAgICBzaG93LmxlZ2VuZCA9ICJwb2ludCIsIHNpemUgPSAuNzUsIGFscGhhPTAuMykgKyANCiAgZ2VvbV9zZihkYXRhID0gUGhpbGx5TGFuZG1hcmtzLCBjb2xvdXIgPSAiYmxhY2siLCBmaWxsPSJibGFjayIsIHNpemUgPSAuNzUsIGFscGhhPTAuNikgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGVlLA0KICAgICAgICAgICAgICAgICAgIGxhYmVscz1xQnIoZGF0YSwgInNhbGVfcHJpY2UiKSwNCiAgICAgICAgICAgICAgICAgICBuYW1lPSJRdWludGlsZSBCcmVha3M6XG5TYWxlIFByaWNlcyIsIA0KICAgICAgICAgICAgICAgICAgIG5hLnZhbHVlID0gTkEpICsgDQogIGxhYnModGl0bGU9IlByb3BlcnRpZXMgQXJvdW5kIExhbmRtYXJrcyIsIHN1YnRpdGxlID0gIlBoaWxhZGVscGhpYSAyMDIyLTIwMjMiLCANCiAgICAgIGNhcHRpb249IkZpZ3VyZSAzLjIuMyIpICsNCiAgbWFwVGhlbWUoKQ0KDQojIE1hcHBpbmcgcHJvcGVydGllcyBhcm91bmQgZmxvb2RwbGFpbnMNCg0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGEgPSB0cmFjdHMyMSwgZmlsbCA9ICJncmV5ODkiLCBjb2xvciA9ICJkYXJrZ3JleSIpICsNCiAgZ2VvbV9zZihkYXRhID0gZGF0YSwgYWVzKGNvbG91ciA9IHE1KHNhbGVfcHJpY2UpKSwgDQogICAgICAgICAgc2hvdy5sZWdlbmQgPSAicG9pbnQiLCBzaXplID0gLjc1LCBhbHBoYT0wLjMpICsgDQogIGdlb21fc2YoZGF0YSA9IFBoaWxseUZsb29kLCBjb2xvdXIgPSAiYmxhY2siLCBmaWxsPSJibGFjayIsIHNpemUgPSAuNzUsIGFscGhhPTAuNikgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGVlLA0KICAgICAgICAgICAgICAgICAgIGxhYmVscz1xQnIoZGF0YSwgInNhbGVfcHJpY2UiKSwNCiAgICAgICAgICAgICAgICAgICBuYW1lPSJRdWludGlsZSBCcmVha3M6XG5TYWxlIFByaWNlcyIsIA0KICAgICAgICAgICAgICAgICAgIG5hLnZhbHVlID0gTkEpICsgDQogIGxhYnModGl0bGU9IlByb3BlcnRpZXMgQXJvdW5kIEZsb29kIFBsYWluIiwgc3VidGl0bGUgPSAiUGhpbGFkZWxwaGlhIDIwMjItMjAyMyIsIA0KICAgICAgY2FwdGlvbj0iRmlndXJlIDMuMi40IikgKw0KICBtYXBUaGVtZSgpDQoNCiNsaWJyYXJ5KGdyaWRFeHRyYSkNCg0KI2dyaWQuYXJyYW5nZSgNCiAgI2IxLA0KICAjYjIsDQogICNiMywNCiAgI2I0LA0KICAjbnJvdyA9IDIsDQogICN3aWR0aHM9Yyg0LDQpLA0KICAjdG9wID0gIlRpdGxlIG9mIHRoZSBwYWdlIg0KICAjKQ0KDQoNCmBgYA0KDQoNCiMjIE1hcHBpbmcgRGVtb2dyYXBoaWMgVmFyaWFibGVzDQoNCmBgYHtyIG1hcCBkZW19DQojIyBNYXBwaW5nIERlbW9ncmFwaGljIFZhcmlhYmxlcw0KDQojIE1hcHBpbmcgbWVkaWFuIGluY29tZSBhcm91bmQgc29sZCBwcm9wZXJ0aWVzDQoNCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhID0gdHJhY3RzMjEsIGZpbGwgPSAiZ3JleTg5IiwgY29sb3IgPSAiZGFya2dyZXkiKSArDQogIGdlb21fc2YoZGF0YSA9IHRyYWN0czIxLCBhZXMoZmlsbCA9IHE1KG1lZF9pbmNvbWUpKSwgY29sb3IgPSAidHJhbnNwYXJlbnQiLCBhbHBoYT0wLjUpICsNCiAgICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIkJ1UHUiLA0KICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IHFCcih0cmFjdHMyMSwgIm1lZF9pbmNvbWUiKSwNCiAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIk1lZGlhbiBJbmNvbWVcblF1aW50aWxlIEJyZWFrcyIpICsNCiAgZ2VvbV9zZihkYXRhID0gZGF0YSwgYWVzKGNvbG91ciA9IHE1KHNhbGVfcHJpY2UpKSwgDQogICAgICAgICAgc2hvdy5sZWdlbmQgPSAicG9pbnQiLCBzaXplID0gLjIpICsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlZSwNCiAgICAgICAgICAgICAgICAgICBsYWJlbHM9cUJyKGRhdGEsICJzYWxlX3ByaWNlIiksDQogICAgICAgICAgICAgICAgICAgbmFtZT0iUXVpbnRpbGUgQnJlYWtzOlxuU2FsZSBQcmljZXMiLCANCiAgICAgICAgICAgICAgICAgICBuYS52YWx1ZSA9IE5BKSArIA0KICBsYWJzKHRpdGxlPSJNZWRpYW4gSW5jb21lIEFyb3VuZCBTb2xkIFByb3BlcnRpZXMiLCBzdWJ0aXRsZSA9ICJQaGlsYWRlbHBoaWEgMjAyMi0yMDIzIiwgDQogICAgICBjYXB0aW9uPSJGaWd1cmUgMy4zLjEiKSsNCiAgbWFwVGhlbWUoKQ0KDQoNCg0KIyBNYXBwaW5nIHdoaXRlIHBvcHVsYXRpb24gYXJvdW5kIHNvbGQgcHJvcGVydGllcw0KDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IHRyYWN0czIxLCBmaWxsID0gImdyZXk4OSIsIGNvbG9yID0gImRhcmtncmV5IikgKw0KICBnZW9tX3NmKGRhdGEgPSB0cmFjdHMyMSwgYWVzKGZpbGwgPSBxNShQY3RXaGl0ZSkpLCBjb2xvciA9ICJ0cmFuc3BhcmVudCIsIGFscGhhPTAuNSkgKw0KICAgIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiQnVQdSIsDQogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gcUJyKHRyYWN0czIxLCAiUGN0V2hpdGUiKSwNCiAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIiUgV2hpdGUgUG9wdWxhdGlvblxuUXVpbnRpbGUgQnJlYWtzIikgKw0KICBnZW9tX3NmKGRhdGEgPSBkYXRhLCBhZXMoY29sb3VyID0gcTUoc2FsZV9wcmljZSkpLCANCiAgICAgICAgICBzaG93LmxlZ2VuZCA9ICJwb2ludCIsIHNpemUgPSAuMikgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGVlLA0KICAgICAgICAgICAgICAgICAgIGxhYmVscz1xQnIoZGF0YSwgInNhbGVfcHJpY2UiKSwNCiAgICAgICAgICAgICAgICAgICBuYW1lPSJRdWludGlsZSBCcmVha3M6XG5TYWxlIFByaWNlcyIsIA0KICAgICAgICAgICAgICAgICAgIG5hLnZhbHVlID0gTkEpICsgDQogIGxhYnModGl0bGU9IiUgV2hpdGUgUG9wdWxhdGlvbiBBcm91bmQgU29sZCBQcm9wZXJ0aWVzIiwgc3VidGl0bGUgPSAiUGhpbGFkZWxwaGlhIDIwMjItMjAyMyIsIA0KICAgICAgY2FwdGlvbj0iRmlndXJlIDMuMy4yIikrDQogIG1hcFRoZW1lKCkNCg0KIyBNYXBwaW5nIGJsYWNrIHBvcHVsYXRpb24gYXJvdW5kIHNvbGQgcHJvcGVydGllcw0KDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IHRyYWN0czIxLCBmaWxsID0gImdyZXk4OSIsIGNvbG9yID0gImRhcmtncmV5IikgKw0KICBnZW9tX3NmKGRhdGEgPSB0cmFjdHMyMSwgYWVzKGZpbGwgPSBxNShQY3RCbGFjaykpLCBjb2xvciA9ICJ0cmFuc3BhcmVudCIsIGFscGhhPTAuNSkgKw0KICAgIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiQnVQdSIsDQogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gcUJyKHRyYWN0czIxLCAiUGN0QmxhY2siKSwNCiAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIiUgQmxhY2sgUG9wdWxhdGlvblxuUXVpbnRpbGUgQnJlYWtzIikgKw0KICBnZW9tX3NmKGRhdGEgPSBkYXRhLCBhZXMoY29sb3VyID0gcTUoc2FsZV9wcmljZSkpLCANCiAgICAgICAgICBzaG93LmxlZ2VuZCA9ICJwb2ludCIsIHNpemUgPSAuMikgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGVlLA0KICAgICAgICAgICAgICAgICAgIGxhYmVscz1xQnIoZGF0YSwgInNhbGVfcHJpY2UiKSwNCiAgICAgICAgICAgICAgICAgICBuYW1lPSJRdWludGlsZSBCcmVha3M6XG5TYWxlIFByaWNlcyIsIA0KICAgICAgICAgICAgICAgICAgIG5hLnZhbHVlID0gTkEpICsgDQogIGxhYnModGl0bGU9IiUgQmxhY2sgUG9wdWxhdGlvbiBBcm91bmQgU29sZCBQcm9wZXJ0aWVzIiwgc3VidGl0bGUgPSAiUGhpbGFkZWxwaGlhIDIwMjItMjAyMyIsIA0KICAgICAgY2FwdGlvbj0iRmlndXJlIDMuMy4zIikrDQogIG1hcFRoZW1lKCkNCg0KIyBNYXBwaW5nIHBvcHVsYXRpb24gd2l0aCBiYWNoZWxvcidzIGRlZ3JlZSBhcm91bmQgc29sZCBwcm9wZXJ0aWVzDQoNCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhID0gdHJhY3RzMjEsIGZpbGwgPSAiZ3JleTg5IiwgY29sb3IgPSAiZGFya2dyZXkiKSArDQogIGdlb21fc2YoZGF0YSA9IHRyYWN0czIxLCBhZXMoZmlsbCA9IHE1KFBjdEJhY2hlbG9ycykpLCBjb2xvciA9ICJ0cmFuc3BhcmVudCIsIGFscGhhPTAuNSkgKw0KICAgIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiQnVQdSIsDQogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gcUJyKHRyYWN0czIxLCAiUGN0QmFjaGVsb3JzIiksDQogICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICIlIEJhY2hlbG9yJ3MgRGVncmVlXG5RdWludGlsZSBCcmVha3MiKSArDQogIGdlb21fc2YoZGF0YSA9IGRhdGEsIGFlcyhjb2xvdXIgPSBxNShzYWxlX3ByaWNlKSksIA0KICAgICAgICAgIHNob3cubGVnZW5kID0gInBvaW50Iiwgc2l6ZSA9IC4yKSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZWUsDQogICAgICAgICAgICAgICAgICAgbGFiZWxzPXFCcihkYXRhLCAic2FsZV9wcmljZSIpLA0KICAgICAgICAgICAgICAgICAgIG5hbWU9IlF1aW50aWxlIEJyZWFrczpcblNhbGUgUHJpY2VzIiwgDQogICAgICAgICAgICAgICAgICAgbmEudmFsdWUgPSBOQSkgKyANCiAgbGFicyh0aXRsZT0iJSBQb3B1bGF0aW9uIHdpdGggQmFjaGVsb3IncyBEZWdyZWUgQXJvdW5kIFNvbGQgUHJvcGVydGllcyIsIHN1YnRpdGxlID0gIlBoaWxhZGVscGhpYSAyMDIyLTIwMjMiLCANCiAgICAgIGNhcHRpb249IkZpZ3VyZSAzLjMuNCIpKw0KICBtYXBUaGVtZSgpDQoNCiMgTWFwcGluZyBwb3B1bGF0aW9uIGluIHBvdmVydHkgYXJvdW5kIHNvbGQgcHJvcGVydGllcw0KDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IHRyYWN0czIxLCBmaWxsID0gImdyZXk4OSIsIGNvbG9yID0gImRhcmtncmV5IikgKw0KICBnZW9tX3NmKGRhdGEgPSB0cmFjdHMyMSwgYWVzKGZpbGwgPSBxNShQY3RQb3ZlcnR5KSksIGNvbG9yID0gInRyYW5zcGFyZW50IiwgYWxwaGE9MC41KSArDQogICAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJCdVB1IiwNCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBxQnIodHJhY3RzMjEsICJQY3RQb3ZlcnR5IiksDQogICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICIlIFBvcCBpbiBQb3ZlcnR5XG5RdWludGlsZSBCcmVha3MiKSArDQogIGdlb21fc2YoZGF0YSA9IGRhdGEsIGFlcyhjb2xvdXIgPSBxNShzYWxlX3ByaWNlKSksIA0KICAgICAgICAgIHNob3cubGVnZW5kID0gInBvaW50Iiwgc2l6ZSA9IC4yKSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZWUsDQogICAgICAgICAgICAgICAgICAgbGFiZWxzPXFCcihkYXRhLCAic2FsZV9wcmljZSIpLA0KICAgICAgICAgICAgICAgICAgIG5hbWU9IlF1aW50aWxlIEJyZWFrczpcblNhbGUgUHJpY2VzIiwgDQogICAgICAgICAgICAgICAgICAgbmEudmFsdWUgPSBOQSkgKyANCiAgbGFicyh0aXRsZT0iJSBQb3B1bGF0aW9uIGluIFBvdmVydHkgQXJvdW5kIFNvbGQgUHJvcGVydGllcyIsIHN1YnRpdGxlID0gIlBoaWxhZGVscGhpYSAyMDIyLTIwMjMiLCANCiAgICAgIGNhcHRpb249IkZpZ3VyZSAzLjMuNSIpKw0KICBtYXBUaGVtZSgpDQoNCmBgYA0KDQoNCg0KIyBTY2F0dGVycGxvdHMgYW5kIENvcnJlbGF0aW9ucyANCg0KIyMgU2NhdHRlcnBsb3RzIA0KDQpUaGUgZm9sbG93aW5nIGNvZGUgZ2VuZXJhdGVzIGEgc2V0IG9mIHNjYXR0ZXJwbG90cyB0byBleHBsb3JlIHRoZSByZWxhdGlvbnNoaXBzIGJldHdlZW4gdGhlIHNhbGUgcHJpY2Ugb2YgaG9tZXMgYW5kIHZhcmlvdXMgY29udGludW91cyB2YXJpYWJsZXMuICBUaGUgZGF0YSBpcyBmaXJzdCBmaWx0ZXJlZCB0byBleGNsdWRlIG9ic2VydmF0aW9ucyB3aGVyZSB0aGUgc2FsZV9wcmljZSBpcyBsZXNzIHRoYW4gMTAsMDAwLDAwMCB0byByZW1vdmUgYW55IG91dGxpZXJzLiBGb3IgdGhlIHJlbWFpbmluZyBkYXRhLCBhIHNldCBvZiBzY2F0dGVycGxvdHMgaXMgY3JlYXRlZC4gRWFjaCBzY2F0dGVycGxvdCBleGFtaW5lcyBob3cgdGhlIHNhbGVfcHJpY2UgcmVsYXRlcyB0byBkaWZmZXJlbnQgY29udGludW91cyB2YXJpYWJsZXMuIFRoZSBzY2F0dGVycGxvdHMgaGVscCB2aXN1YWxpemUgdGhlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB0aGUgc2FsZSBwcmljZSBvZiBob21lcyBhbmQgZWFjaCBvZiB0aGUgY29udGludW91cyB2YXJpYWJsZXMsIGFzIHdlbGwgYXMgdGhlIGRpcmVjdGlvbiBhbmQgc3RyZW5ndGggb2YgdGhlc2UgcmVsYXRpb25zaGlwcy4gVGhlIHJlZ3Jlc3Npb24gbGluZXMgaW5kaWNhdGUgd2hldGhlciB0aGVyZSBpcyBhIGxpbmVhciB0cmVuZCBpbiB0aGUgZGF0YS4NCg0KYGBge3Igc2NhdHRlcnBsb3RzLCBmaWcuYWxpZ249ImNlbnRlciIsIHdhcm5pbmc9RkFMU0UsIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoID0gMTQsIGZpZy5oZWlnaHQ9MjggfQ0Kb3B0aW9ucyhzY2lwZW4gPSA5OTk5KSAjdHVybnMgb2ZmIHNjaWVudGlmaWMgbm90YXRpb24gYW5kIGhvdyBtYW55IHBvaW50cw0KDQpWYXJpYWJsZSA8LSBkYXRhICU+JQ0KICBmaWx0ZXIoc2FsZV9wcmljZSA8IDEwMDAwMDAwKQ0KDQojIyBTY2F0dGVycGxvdA0Kc3RfZHJvcF9nZW9tZXRyeShWYXJpYWJsZSkgJT4lIA0KICBkcGx5cjo6c2VsZWN0KHNhbGVfcHJpY2UsIFByaWNlUGVyU3FmdCwgeWVhcl9idWlsdCwgbnVtYmVyX29mX2JhdGhyb29tcywgbnVtYmVyX29mX2JlZHJvb21zLCBudW1iZXJfb2Zfcm9vbXMsIHRvdGFsX2xpdmFibGVfYXJlYSwgbWVkX2luY29tZSwgUGN0V2hpdGUsUGN0QmxhY2ssIFBjdEhpc3BhbmljLCBQY3RCYWNoZWxvcnMsIFBjdFBvdmVydHksICkgJT4lIA0KICBmaWx0ZXIoc2FsZV9wcmljZSA8IDEwMDAwMDAwKSAlPiUNCiAgZ2F0aGVyKFZhcmlhYmxlLCBWYWx1ZSwgLXNhbGVfcHJpY2UpICU+JSANCiAgIGdncGxvdChhZXMoVmFsdWUsIHNhbGVfcHJpY2UpKSArDQogICAgIGdlb21fcG9pbnQoc2l6ZSA9IC41KSArIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlPUYsIGNvbG91ciA9ICIjRkE3ODAwIikgKw0KICAgICBmYWNldF93cmFwKH5WYXJpYWJsZSwgbmNvbCA9IDIsIHNjYWxlcyA9ICJmcmVlIikgKw0KICAgICBsYWJzKHRpdGxlID0gIlByaWNlIGFzIGEgZnVuY3Rpb24gb2YgY29udGludW91cyB2YXJpYWJsZXMiLCANCiAgICAgICAgICBjYXB0aW9uPSJGaWd1cmUgNC4xIikgKw0KICAgICBwbG90VGhlbWUoKQ0KYGBgDQoNCiMjIENvcnJlbGF0aW9ucw0KDQpUaGUgcHJvY2VzcyBvZiB2YXJpYWJsZSBzZWxlY3Rpb24gZm9yIHRoZSBtb2RlbCB3YXMgYW4gaXRlcmF0aXZlIG9uZSwgYmVnaW5uaW5nIHdpdGggYW4gaW5pdGlhbCBleHBsb3JhdGlvbiBvZiBpbnRlci12YXJpYWJsZSByZWxhdGlvbnNoaXBzLiBGb2xsb3dpbmcgb3VyIHN0cmF0ZWd5IG9mIGNhdGVnb3JpemluZyB2YXJpYWJsZXMgaW50byB0aHJlZSBvdmVyYXJjaGluZyBncm91cHPigJRJbnRlcm5hbCwgU3BhdGlhbCwgYW5kIERlbW9ncmFwaGlj4oCUd2UgcHJvY2VlZGVkIHRvIGV2YWx1YXRlIGFuZCB2aXN1YWxpemUgdGhlIGNvcnJlbGF0aW9ucyBiZXR3ZWVuIHRoZXNlIHZhcmlhYmxlcyBhbmQgdGhlIGBzYWxlX3ByaWNlYC4gQnJvYWRseSBzcGVha2luZywgd2Ugb2JzZXJ2ZWQgdGhhdCBpbnRlcm5hbCBhbmQgZGVtb2dyYXBoaWMgdmFyaWFibGVzIGV4aGliaXRlZCB0aGUgbW9zdCBwcm9ub3VuY2VkIGNvcnJlbGF0aW9ucyB3aXRoIGBzYWxlX3ByaWNlYCwgd2l0aCBvbmx5IGEgbGltaXRlZCBudW1iZXIgb2Ygc3BhdGlhbCB2YXJpYWJsZXMgZGVtb25zdHJhdGluZyBub3Rld29ydGh5IGFzc29jaWF0aW9ucyB3aXRoIHRoZSBpbmRlcGVuZGVudCB2YXJpYWJsZXMgYHNhbGVfcHJpY2VgLg0KDQojIyMgQ29ycmVsYXRpb24gTWF0cml4OiBJbnRlcm5hbCBWYXJpYWJsZXMNCg0KRm9yIHRoaXMgc3RlcCwgYSBsaXN0IG9mIGltcG9ydGFudCBpbnRlcm5hbCB2YXJpYWJsZXMgZm9yIHJlYWwgZXN0YXRlLCBsaWtlIHNhbGUgcHJpY2UsIHByb3BlcnR5IHNpemUsIGFuZCB2YXJpb3VzIGZlYXR1cmVzLCBpcyBkZWZpbmVkIGluIHRoZSAnaW50ZXJuYWx2YXJpYWJsZXMnIGxpc3QuIFRoZSBjb2RlIHRoZW4gcHJvY2Vzc2VzIHRoZSBkYXRhLCBwaWNraW5nIG91dCBvbmx5IHRoZSBudW1iZXJzIGZyb20gdGhlc2UgaW50ZXJuYWwgdmFyaWFibGVzIGFuZCByZW1vdmluZyBhbnkgcm93cyB3aXRoIG1pc3NpbmcgZGF0YS4gQWZ0ZXIgdGhhdCwgaXQgY2FsY3VsYXRlcyBob3cgdGhlc2UgaW50ZXJuYWwgdmFyaWFibGVzIGFyZSByZWxhdGVkIHRvIGVhY2ggb3RoZXIgdXNpbmcgYSBjb3JyZWxhdGlvbiBtYXRyaXguDQoNClVwb24gYW5hbHl6aW5nIHRoZSByZXN1bHRzLCB3ZSBvYnNlcnZlIHRoYXQgdmFyaWFibGVzIHN1Y2ggYXMgcHJpY2UgcGVyIHNxdWFyZSBmb290LCBudW1iZXIgb2Ygcm9vbXMsIGFuZCB0b3RhbCBsaXZhYmxlIGFyZWEgZXhoaWJpdCBzdHJvbmcgY29ycmVsYXRpb25zIHdpdGggdGhlIHNhbGUgcHJpY2UuIEhvd2V2ZXIsIGl0J3MgZXNzZW50aWFsIHRvIGtlZXAgaW4gbWluZCB0aGF0IHRoZSBwcmljZSBwZXIgc3F1YXJlIGZvb3QgaXMgZGVyaXZlZCBmcm9tIHRoZSBzYWxlIHByaWNlLCB3aGljaCBleHBsYWlucyB0aGUgaGlnaCBjb3JyZWxhdGlvbi4gU2ltaWxhcmx5LCBmb3IgdGhlIG51bWJlciBvZiByb29tcyBhbmQgbnVtYmVyIG9mIGJlZHJvb21zLCBjb25zaWRlcmluZyB0aGUgaW1wdXRhdGlvbnMgbWFkZSBlYXJsaWVyIGluIHRoaXMgYW5hbHlzaXMsIGl0IGJlY29tZXMgYSBxdWVzdGlvbmFibGUgY2hvaWNlIHRvIGluY2x1ZGUgdGhlbSBpbiB0aGUgZmluYWwgcmVncmVzc2lvbiBhbmFseXNpcyBkdWUgdG8gdGhlIGhpZ2ggbnVtYmVyIG9mIG1pc3NpbmcgdmFsdWVzIGFuZCB0aGUgZ2VuZXJhbCB1bmNlcnRhaW50eSBzdXJyb3VuZGluZyB0aGUgYXNzaWduZWQgdmFsdWVzLCBlc3BlY2lhbGx5IGdpdmVuIHRoZSBoaWdoIGNvcnJlbGF0aW9uIHdpdGggYW5vdGhlciB2YXJpYWJsZSwgYHRvdGFsX2xpdmluZ19hcmVhYCwgd2hpY2ggaXMgYWxyZWFkeSBpbmNsdWRlZCBpbiB0aGUgYW5hbHlzaXMuDQoNClRoZSBmaW5hbCBpbnRlcm5hbCB2YXJpYWJsZXMgc2VsZWN0ZWQgZm9yIHRoaXMgYW5hbHlzaXMgaW5jbHVkZTogYHRvdGFsX2xpdmFibGVfYXJlYWAsYG51bWJlcl9vZl9iYXRocm9vbXNgLCBgZmlyZXBsYWNlc2AsIGBleHRlcmlvcl9jb25kaXRpb25gLCBhbmQgYGNlbnRyYWxfYWlyYC4NCg0KYGBge3IgY29yciBpbnR9DQppbnRlcm5hbHZhcmlhYmxlcyA8LSBjKA0KICAic2FsZV9wcmljZSIsICJQcmljZVBlclNxZnQiLCAidG90YWxfbGl2YWJsZV9hcmVhIiwgIm51bWJlcl9vZl9yb29tcyIsICJudW1iZXJfb2ZfYmF0aHJvb21zIiwgIm51bWJlcl9vZl9iZWRyb29tcyIsICJCdWlsZGluZ0FnZSIsICJmcm9udGFnZSIsICJkZXB0aCIsICJmaXJlcGxhY2VzIiwgIkJhc2VtZW50UHJlc2VudCIsICJHYXJhZ2VQcmVzZW50IiwgImV4dGVyaW9yX2NvbmRpdGlvbiIsICJudW1iZXJfc3RvcmllcyIsICJjZW50cmFsX2FpciINCikNCg0KbnVtZXJpY2ludFZhcnMgPC0gZGF0YSAlPiUNCiAgc3RfZHJvcF9nZW9tZXRyeShkYXRhKSAlPiUNCiAgc2VsZWN0KGludGVybmFsdmFyaWFibGVzKSU+JQ0KICBuYS5vbWl0KCkNCg0KZ2djb3JycGxvdCgNCiAgcm91bmQoY29yKG51bWVyaWNpbnRWYXJzKSwgMSksIA0KICBwLm1hdCA9IGNvcl9wbWF0KG51bWVyaWNpbnRWYXJzKSwNCiAgY29sb3JzID0gYygnI2Q3MTkxYycsJyNmZmZmYmYnLCcjMmM3YmI2JyksDQogIHR5cGU9Imxvd2VyIiwNCiAgaW5zaWcgPSAiYmxhbmsiKSArICANCiAgICBsYWJzKHRpdGxlID0gIkNvcnJlbGF0aW9uIGFjcm9zcyBJbnRlcm5hbCBWYXJpYWJsZXMiKSANCg0KYGBgDQoNCg0KIyMjIENvcnJlbGF0aW9uIE1hdHJpeDogU3BhdGlhbCBWYXJpYWJsZXMNCg0KVW5leHBlY3RlZGx5LCBkdXJpbmcgb3VyIGFuYWx5c2lzLCB3ZSBkaXNjb3ZlcmVkIHRoYXQgdGhlcmUgd2VyZSByZWxhdGl2ZWx5IGZldyBzdHJvbmcgcG9zaXRpdmUgb3IgbmVnYXRpdmUgY29ycmVsYXRpb25zIGFtb25nIHRoZSBzcGF0aWFsIGVsZW1lbnRzIHdlIGV4YW1pbmVkLiBVbmxpa2UgdGhlIGRlbW9ncmFwaGljcyBhbmQgaW50ZXJuYWwgdmFyaWFibGVzLCB3aGljaCBkZW1vbnN0cmF0ZWQgc2lnbmlmaWNhbnQgaW5mbHVlbmNlIG9uIHRoZSBzYWxlIHByaWNlLCB0aGVzZSBzcGF0aWFsIGZhY3RvcnMgc2VlbWVkIHRvIGhhdmUgbGVzcyBpbXBhY3QuIERlc3BpdGUgb3VyIGJlc3QgZWZmb3J0cyBhbmQgYXR0ZW1wdHMgdG8gYmUgaW5ub3ZhdGl2ZSBpbiBvdXIgdmFyaWFibGUgc2VsZWN0aW9uLCB0aGUgb3ZlcmFsbCBjb3JyZWxhdGlvbnMgYXBwZWFyZWQgdG8gYmUgbGVzcyBwcm9ub3VuY2VkIGluIHRoaXMgY29udGV4dC4gVGhpcyBvYnNlcnZhdGlvbiBtaWdodCBhbHNvIGJlIGxpbmtlZCB0byB0aGUgYWJzZW5jZSBvZiBlcnJvciBjbHVzdGVyaW5nIHRoYXQgd2UgaWRlbnRpZmllZCBsYXRlciBpbiBvdXIgYW5hbHlzaXMuIElmIHRpbWUgd2FzIG5vIGNvbnRyYWludCwgd2Ugd291bGQgaGF2ZSBleHBsb3JlZCBvdGhlciBzcGF0aWFsIHZhcmlibGVzIGZ1cnRoZXIsIGhvcGluZyB0byBmaW5kIG1vcmUgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBvbmVzLiAgDQoNCg0KYGBge3IgY29yciBzcGF0fQ0Kc3BhdHZhcmlhYmxlcyA8LSBjKCJzYWxlX3ByaWNlIiwgImRpc3RfdG9fbmVhcmVzdF9zY2hvb2wiLCAiZGlzdF90b19uZWFyZXN0X3B2dF9zY2hvb2wiLCAiZGlzdF90b19uZWFyZXN0X2xhbmRtYXJrIiwgImRpc3RfdG9fY29tbV9jb3JyIiwgImRpc3RfdG9fbmVhcmVzdF9zdGF0aW9uIiwgImRpc3RfdG9fbmVhcmVzdF90cmFpbCIsICJkaXN0X3RvX25lYXJlc3RfcG9saWNlX3N0YXRpb24iLCJjcmltZV9ubjUiLCAibGl0dGVyIiwgIndpdGhpbl9jb21fY29yciIsICJ3aXRoaW5fZmxvb2QiLCAiZGlzdF90b19mbG9vZHBsYWluIiwgImRpc3RfdG9fbmVhcmVzdF90ZW5uaXNjb3VydCIsICJkaXN0X3RvX25lYXJlc3RfcGxheWdyb3VuZCIsICJkaXN0X3RvX25lYXJlc3RfcG9vbCIgIA0KICApDQoNCm51bWVyaWNzcGF0VmFycyA8LSBkYXRhICU+JQ0KICBzdF9kcm9wX2dlb21ldHJ5KGRhdGEpICU+JQ0KICBzZWxlY3QoYWxsX29mKHNwYXR2YXJpYWJsZXMpKSU+JQ0KICBuYS5vbWl0KCkNCg0KZ2djb3JycGxvdCgNCiAgcm91bmQoY29yKG51bWVyaWNzcGF0VmFycyksIDEpLCANCiAgcC5tYXQgPSBjb3JfcG1hdChudW1lcmljc3BhdFZhcnMpLA0KICBjb2xvcnMgPSBjKCcjZDcxOTFjJywnI2ZmZmZiZicsJyMyYzdiYjYnKSwNCiAgdHlwZT0ibG93ZXIiLA0KICBpbnNpZyA9ICJibGFuayIpICsgIA0KICAgIGxhYnModGl0bGUgPSAiQ29ycmVsYXRpb24gYWNyb3NzIFNwYXRpYWwgVmFyaWFibGVzIikgDQoNCmBgYA0KDQojIyMgQ29ycmVsYXRpb24gTWF0cml4OiBEZW1vZ3JhcGhpYyBWYXJpYWJsZXMNCg0KDQpJbiB0aGlzIHNlY3Rpb24gb2YgdGhlIGNvZGUsIHdlIGNvbXBpbGUgYSBsaXN0IG9mIHNpZ25pZmljYW50IGRlbW9ncmFwaGljIHZhcmlhYmxlcywgaW5jbHVkaW5nIGZhY3RvcnMgc3VjaCBhcyByYWNpYWwgY29tcG9zaXRpb24sIGVkdWNhdGlvbmFsIGF0dGFpbm1lbnQsIGluY29tZSBsZXZlbHMsIGhvdXNpbmcsIGFuZCBtb3JlLiBVcG9uIGFuYWx5emluZyB0aGUgcmVzdWx0cywgdGhlIGdlbmVyYXRlZCBjb3JyZWxhdGlvbiBwbG90IGlsbHVzdHJhdGVzIGhvdyB0aGVzZSBkZW1vZ3JhcGhpYyBmYWN0b3JzIGludGVycmVsYXRlLiANCg0KSW4gb3VyIGFuYWx5c2lzLCB3ZSBvYnNlcnZlIGEgbmVhcmx5IHB1cmUgbmVnYXRpdmUgY29ycmVsYXRpb24gYmV0d2VlbiAncGVyY2VudCB3aGl0ZScgYW5kICdwZXJjZW50IGJsYWNrLicgVG8gbWl0aWdhdGUgbXVsdGljb2xsaW5lYXJpdHksIHdlIHJlY29tbWVuZCBpbmNsdWRpbmcgb25seSBvbmUgb2YgdGhlc2UgdmFyaWFibGVzIGluIHRoZSBmaW5hbCBtb2RlbC4gJ01lZGlhbiBpbmNvbWUnIGRpc3BsYXlzIGEgcm9idXN0IGFuZCBoaWdobHkgcG9zaXRpdmUgY29ycmVsYXRpb24gd2l0aCAnc2FsZSBwcmljZSwnIG1ha2luZyBpdCBhIHZhbHVhYmxlIGFkZGl0aW9uIHRvIHRoZSBtb2RlbCwgZ2l2ZW4gaXRzIHN0cm9uZyBwcmVkaWN0aXZlIHBvd2VyLiBTaW1pbGFybHksICdtb250aGx5IGNvc3QsJyB3aGljaCByZXByZXNlbnRzIHRoZSBjb3N0IG9mIHV0aWxpdGllcyBwZXIgbW9udGggaW4gaG91c2VzIHdpdGggYSBtb3J0Z2FnZSwgZXhoaWJpdHMgYSBub3Rld29ydGh5IHBvc2l0aXZlIGNvcnJlbGF0aW9uIHdpdGggJ3NhbGUgcHJpY2UsJyBtYWtpbmcgaXQgYW5vdGhlciB3b3J0aHkgaW5jbHVzaW9uIGluIHRoZSBmaW5hbCBtb2RlbCwgYXMgaXQgcHJvbWlzZXMgdG8gZW5oYW5jZSBvdXIgcHJlZGljdGl2ZSBjYXBhYmlsaXRpZXMuDQoNCmBgYHtyIGNvcnIgZGVtfQ0KZGVtdmFyaWFibGVzIDwtIGMoDQogICJzYWxlX3ByaWNlIiwgIlBjdFdoaXRlIiwgIlBjdEJsYWNrIiwgIlBjdEhpc3BhbmljIiwgIlBjdEJhY2hlbG9ycyIsICJQY3RQb3ZlcnR5IiwgIm1lZF9pbmNvbWUiLA0KICANCiAgIm1lZF9yZW50IiwgIm93bmVyX3VuaXRzIiwgICAgICAgICAicmVudGVyX3VuaXRzIiANCiAgICAgICAgLCAic2FtZV9ob3VzZV83NSIgDQogICAgICAgLCAgInNhbWVfaG91c2VfMSIgDQogICAgICAsICAgIm1vbnRobHlfY29zdHNfbm9fbW9ydGdhZ2UiIA0KICAgICAgLCAgICJtb250aGx5X2Nvc3RzX3dpdGhfbW9ydGdhZ2UiIA0KICAgICAgICAsICJtZWRfZ3Jvc3NfcmVudCIgICAgIA0KICAgICAgICAsICJob3VzaW5nX3VuaXRzX3dpdGhfaGVhdCIgDQogICAgICAgICwgImVkdV9iYWNoZWxvcnMiIA0KICAgICAgICwgICJwb3BfYmVsb3dfcG92ZXJ0eSIgDQogICAgICAgLCAgImhvdXNpbmdfdW5pdHNfaGlnaF9zcGVlZF9pbnRlcm5ldCIgDQogICAgICAgLCAgImhvdXNpbmdfdW5pdHNfbm9fdmVoaWNsZSIgICAgICAgDQogICkNCg0KbnVtZXJpY2RlbVZhcnMgPC0gZGF0YSAlPiUNCiAgc3RfZHJvcF9nZW9tZXRyeShkYXRhKSAlPiUNCiAgc2VsZWN0KGRlbXZhcmlhYmxlcyklPiUNCiAgbmEub21pdCgpDQoNCmdnY29ycnBsb3QoDQogIHJvdW5kKGNvcihudW1lcmljZGVtVmFycyksIDEpLCANCiAgcC5tYXQgPSBjb3JfcG1hdChudW1lcmljZGVtVmFycyksDQogIGNvbG9ycyA9IGMoJyNkNzE5MWMnLCcjZmZmZmJmJywnIzJjN2JiNicpLA0KICB0eXBlPSJsb3dlciIsDQogIGluc2lnID0gImJsYW5rIikgKyAgDQogICAgbGFicyh0aXRsZSA9ICJDb3JyZWxhdGlvbiBhY3Jvc3MgRGVtb2dyYXBoaWMgVmFyaWFibGVzIikgDQoNCmBgYA0KDQojIyMgRmluYWwgVmFyaWFibGUgU2VsZWN0aW9uDQoNCkJhc2VkIG9uIG91ciBpbml0aWFsIGNvcnJlbGF0aW9uIGFuYWx5c2VzLCB3ZSB3ZW50IHRocm91Z2ggYW4gaXRlcmF0aXZlIHByb2Nlc3MgdG8gZGV0ZXJtaW5lIHRoZSBmaW5hbCBzZXQgb2YgbWV0cmljcyBmb3Igb3VyIG1vZGVsLiBUaGUgY29kZSBpbml0aWFsbHkgaW5jbHVkZWQgc29tZSB2YXJpYWJsZXMgd2UgY29uc2lkZXJlZCwgYnV0IHRvIG1pbmltaXplIGVycm9ycyBsaWtlIE1BUEUgYW5kIE1BRSB3aGlsZSBtYXhpbWl6aW5nIGFjY3VyYWN5LCB3ZSBtYWRlIHNvbWUgc3Vic3RpdHV0aW9ucy4gV2UgYWxzbyBhaW1lZCB0byBlbmhhbmNlIHRoZSBtb2RlbCdzIGdlbmVyYWxpemFiaWxpdHkgYnkgY2FyZWZ1bGx5IGNob29zaW5nIGFuZCByZWplY3Rpbmcgc3BlY2lmaWMgdmFyaWFibGVzLiBGaW5hbGx5LCB3ZSBzZXR0bGVkIG9uIHRoZSB2YXJpYWJsZXMgcHJlc2VudGVkLiBUaGlzIGNvbXByZWhlbnNpdmUgYXBwcm9hY2ggYWxsb3dlZCB1cyB0byB0YWlsb3Igb3VyIG1vZGVsJ3MgdmFyaWFibGUgc2VsZWN0aW9uIHRvIG5vdCBvbmx5IHJlZHVjZSBlcnJvcnMgYnV0IGFsc28gZW5zdXJlIHRoZSBtb2RlbCdzIGFwcGxpY2FiaWxpdHkgdG8gYSB3aWRlIHJhbmdlIG9mIHNjZW5hcmlvcywgdWx0aW1hdGVseSBjb250cmlidXRpbmcgdG8gbW9yZSByb2J1c3QgYW5kIGRlcGVuZGFibGUgcmVzdWx0cyBpbiBvdXIgcmVncmVzc2lvbiBhbmFseXNpcy4NCg0KYGBge3IgY29yciBhbGx9DQoNCiN2YXJpYWJsZXMgPC0gYygic2FsZV9wcmljZSIsICJudW1iZXJfb2ZfYmF0aHJvb21zIiwiZGlzdF90b19uZWFyZXN0X3B2dF9zY2hvb2wiLCAiZGlzdF90b19jb21tX2NvcnIiLCAiZGlzdF90b19uZWFyZXN0X3N0YXRpb24iLCAiZGlzdF90b19uZWFyZXN0X3RyYWlsIiwgIm1lZF9pbmNvbWUiLCAiZGlzdF90b19uZWFyZXN0X3Bvb2wiLCAiZnJvbnRhZ2UiLCAiY2VudHJhbF9haXIiLCAiZmlyZXBsYWNlcyIsICJCYXNlbWVudFByZXNlbnQiLCAiZXh0ZXJpb3JfY29uZGl0aW9uIiwgIlBjdFdoaXRlIiwgIlBjdEJsYWNrIiwgInRvdGFsX2xpdmFibGVfYXJlYSIsICJCdWlsZGluZ0FnZSIsICAgIlBjdEJhY2hlbG9ycyIsICJQY3RQb3ZlcnR5IiwgIlByaWNlUGVyU3FmdCIsInRvdGFsX2xpdmFibGVfYXJlYSIsICJob3VzaW5nX3VuaXRzX3dpdGhfaGVhdCIsICJQY3RIaXNwYW5pYyIsICJob3VzaW5nX3VuaXRzX2hpZ2hfc3BlZWRfbnRlcm5ldCIpDQoNCnZhcmlhYmxlcyA8LSBjICgic2FsZV9wcmljZSIgLCJ0b3RhbF9saXZhYmxlX2FyZWEiLCJudW1iZXJfb2ZfYmF0aHJvb21zIiwgImZpcmVwbGFjZXMiLCAiZXh0ZXJpb3JfY29uZGl0aW9uIiwgImNlbnRyYWxfYWlyIiwgIlBjdFdoaXRlIiwgIm1lZF9pbmNvbWUiLCAibW9udGhseV9jb3N0c193aXRoX21vcnRnYWdlIiAsICJQY3RQb3ZlcnR5IiwgImRpc3RfdG9fZmxvb2RwbGFpbiIsICJ3aXRoaW5fY29tX2NvcnIiKQ0KDQojZHJvcHBpbmcgY3JpbWUgZm9yIG5vdyBjaGVjayBsYXRlciANCg0KbnVtZXJpY1ZhcnMgPC0gZGF0YSAlPiUNCiAgc3RfZHJvcF9nZW9tZXRyeShkYXRhKSAlPiUNCiAgc2VsZWN0KHZhcmlhYmxlcyklPiUNCiAgbmEub21pdCgpDQoNCmdnY29ycnBsb3QoDQogIHJvdW5kKGNvcihudW1lcmljVmFycyksIDEpLCANCiAgcC5tYXQgPSBjb3JfcG1hdChudW1lcmljVmFycyksDQogIGNvbG9ycyA9IGMoJyNkNzE5MWMnLCcjZmZmZmJmJywnIzJjN2JiNicpLA0KICB0eXBlPSJsb3dlciIsDQogIGluc2lnID0gImJsYW5rIikgKyAgDQogICAgbGFicyh0aXRsZSA9ICJDb3JyZWxhdGlvbiBhY3Jvc3MgbnVtZXJpYyB2YXJpYWJsZXMiKSANCmBgYA0KDQojIE9MUyBSZWdyZXNzc2lvbiBNb2RlbA0KDQpUaGlzIGNvZGUgdXRpbGl6ZXMgYW4gT0xTIHJlZ3Jlc3Npb24gbW9kZWwgdG8gbWFrZSBhY2N1cmF0ZSBwcmVkaWN0aW9ucyBhYm91dCBob21lIHByaWNlcy4gVGhlIGFkdmFudGFnZSBvZiBhbiBPTFMgbW9kZWwgaXMgaXRzIHNpbXBsaWNpdHkgYW5kIGVhc2Ugb2YgaW50ZXJwcmV0YXRpb24sIG1ha2luZyBpdCBhIHZhbHVhYmxlIHRvb2wgZm9yIHVuZGVyc3RhbmRpbmcgbGluZWFyIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB2YXJpYWJsZXMuDQoNCiMjIFRlc3RpbmcgYW5kIFRyYWluaW5nIERhdGEgDQoNCkZpcnN0IHdlIHNwbGl0IHRoZSBtb2RlbGxpbmcgZGF0YSBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0aW5nIHNldHMsIGVuc3VyaW5nIHRoYXQgdGhlIGRhdGEgaXMgc3BsaXQgcmFuZG9tbHkgZWFjaCB0aW1lLiANCg0KYGBge3Igc3BsaXRkYXRhfQ0KDQojIyBDcmVhdGluZyB0aGUgdHJhaW5pbmcgYW5kIHRlc3Qgc2V0DQptb2RlbGxpbmdfZGF0YSA8LSBkYXRhICU+JSBmaWx0ZXIodG9QcmVkaWN0ID09ICJNT0RFTExJTkciKQ0KDQpzZXQuc2VlZCg5OTkpICNtYWtlcyBzdXJlIGRhdGEgaXMgc3BsaXQgc2FtZSB3YXkgZXZlcnkgdGltZQ0KDQojIyBTcGxpdHRpbmcgdGhlIGRhdGENCnNwbGl0IDwtIHNhbXBsZS5zcGxpdChtb2RlbGxpbmdfZGF0YSRvYmplY3RpZCwgU3BsaXRSYXRpbyA9IDAuNzUpDQoNCiMjIENyZWF0aW5nIHRoZSB0cmFpbmluZyBhbmQgdGVzdCBzZXRzDQp0cmFpbl9kYXRhIDwtIG1vZGVsbGluZ19kYXRhW3NwbGl0LF0NCnRlc3RfZGF0YSA8LSBtb2RlbGxpbmdfZGF0YVshc3BsaXQsXQ0KYGBgDQoNCiMjIFJlZ3Jlc3Npb24gQW5hbHlzaXMNCg0KQWZ0ZXIgcnVubmluZyB0aGUgcmVncmVzc2lvbiBhbmFseXNpcywgd2Ugd2VyZSBhYmxlIHRvIGNvbWUgdG8gc29tZSBpbnRlcmVzdGluZyByZWxhdGlvbnMgYmV0d2VlbiB0aGUgc2FsZSBwcmljZSBhbmQgZGlmZmVyZW50IHZhcmlhYmxlcyB1c2VkIGluIHRoZSBhbmFseXNpcy4gDQoNCjEuIFRoZSBjb2VmZmljaWVudHMgZm9yICd0b3RhbF9saXZhYmxlX2FyZWEsJyAnbnVtYmVyX29mX2JhdGhyb29tcywnICdmaXJlcGxhY2VzLCcgJ2V4dGVyaW9yX2NvbmRpdGlvbiwnICdjZW50cmFsX2FpciwnICdQY3RXaGl0ZSwnICdtZWRfaW5jb21lLCcgJ21vbnRobHlfY29zdHNfd2l0aF9tb3J0Z2FnZSwnIGFuZCAnd2l0aGluX2NvbV9jb3JyJyBhcmUgYWxsIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQuIA0KDQoyLiBUb3RhbCBMaXZhYmxlIEFyZWEgKHRvdGFsX2xpdmFibGVfYXJlYSk6IFRoZSBjb2VmZmljaWVudCBvZiBhcHByb3hpbWF0ZWx5IDE4Mi40NCBzdWdnZXN0cyB0aGF0LCBmb3IgZWFjaCBhZGRpdGlvbmFsIHVuaXQgb2YgbGl2YWJsZSBhcmVhLCB0aGUgJ3NhbGVfcHJpY2UnIGlzIGV4cGVjdGVkIHRvIGluY3JlYXNlIGJ5IGFyb3VuZCAxODIuNDQgdW5pdHMsIHByb3ZpZGVkIHRoYXQgYWxsIG90aGVyIHZhcmlhYmxlcyByZW1haW4gY29uc3RhbnQuIFRoaXMgaW5kaWNhdGVzIGEgc3Ryb25nIHBvc2l0aXZlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBzaXplIG9mIHRoZSBwcm9wZXJ0eSBhbmQgaXRzIHNhbGUgcHJpY2UuDQoNCjMuIE51bWJlciBvZiBCYXRocm9vbXMgKG51bWJlcl9vZl9iYXRocm9vbXMpOiBXaXRoIGEgY29lZmZpY2llbnQgb2YgYXBwcm94aW1hdGVseSA1ODY1OS4wNCwgZWFjaCBhZGRpdGlvbmFsIGJhdGhyb29tIGlzIGFzc29jaWF0ZWQgd2l0aCBhIHN1YnN0YW50aWFsIGluY3JlYXNlIGluICdzYWxlX3ByaWNlLicgVGhpcyBpbXBsaWVzIHRoYXQgcHJvcGVydGllcyB3aXRoIG1vcmUgYmF0aHJvb21zIHRlbmQgdG8gY29tbWFuZCBoaWdoZXIgcHJpY2VzLg0KDQo0LiBFeHRlcmlvciBDb25kaXRpb24gKGV4dGVyaW9yX2NvbmRpdGlvbik6IFRoZSBjb2VmZmljaWVudCBvZiBhcm91bmQgLTIxNzQ5LjgwIHNpZ25pZmllcyB0aGF0LCBhcyB0aGUgY29uZGl0aW9uIG9mIHRoZSBleHRlcmlvciBkZXRlcmlvcmF0ZXMsICdzYWxlX3ByaWNlJyB0ZW5kcyB0byBkZWNyZWFzZS4gVGhpcyBuZWdhdGl2ZSBjb2VmZmljaWVudCBzdWdnZXN0cyB0aGF0IGEgd2VsbC1tYWludGFpbmVkIGV4dGVyaW9yIGhhcyBhIHBvc2l0aXZlIGltcGFjdCBvbiB0aGUgcHJvcGVydHkncyB2YWx1ZS4NCg0KNS4gTW9udGhseSBDb3N0cyB3aXRoIE1vcnRnYWdlIChtb250aGx5X2Nvc3RzX3dpdGhfbW9ydGdhZ2UpOiBUaGUgY29lZmZpY2llbnQgb2YgYXBwcm94aW1hdGVseSAxNDEuMTQgc3VnZ2VzdHMgdGhhdCBhbiBpbmNyZWFzZSBpbiB1dGlsaXR5IGNvc3RzIHBlciBtb250aCBvbiBob3VzZXMgd2l0aCBhIG1vcnRnYWdlIGxlYWRzIHRvIGEgaGlnaGVyICdzYWxlX3ByaWNlLicgVGhpcyBpbmRpY2F0ZXMgdGhhdCBidXllcnMgbWF5IGJlIHdpbGxpbmcgdG8gcGF5IG1vcmUgZm9yIHByb3BlcnRpZXMgYXNzb2NpYXRlZCB3aXRoIGhpZ2hlciBtb250aGx5IGNvc3RzLCBwb3NzaWJseSBkdWUgdG8gcGVyY2VpdmVkIHZhbHVlIG9yIGFtZW5pdGllcy4NCg0KNi4gV2l0aGluIENvbW11bml0eSBDb3JyZWxhdGlvbiAod2l0aGluX2NvbV9jb3JyKTogV2l0aCBhIGNvZWZmaWNpZW50IG9mIHJvdWdobHkgMzU2MjkuODMsIGEgaGlnaGVyIHdpdGhpbi1jb21tdW5pdHkgY29ycmVsYXRpb24gaXMgYXNzb2NpYXRlZCB3aXRoIGFuIGluY3JlYXNlIGluICdzYWxlX3ByaWNlLicgVGhpcyBpbXBsaWVzIHRoYXQgcHJvcGVydGllcyBpbiBhcmVhcyB3aGVyZSBob3VzaW5nIHByaWNlcyBhcmUgcG9zaXRpdmVseSBjb3JyZWxhdGVkIHRlbmQgdG8gY29tbWFuZCBoaWdoZXIgcHJpY2VzLCByZWZsZWN0aW5nIHRoZSBkZXNpcmFiaWxpdHkgb2Ygc3VjaCBuZWlnaGJvcmhvb2RzLg0KDQpXZSBhbHNvIHRoZW4gdXNlIHRoZSByZWdyZXNzaW9uIHRvIHByZWRpY3QgaG9tZSBwcmljZXMgZm9yIHRoZSB0ZXN0aW5nIGRhdGFzZXQsIHRvIHRoZW4gcGVyZm9ybSBhY2N1cmFjeSBhbmQgZ2VuZXJhbGl6YWJsaXR5IHRlc3RzIG9uIGl0IHRvIHJlZmluZSB0aGUgcmVncmVzc2lvbiBhbmQgY2xlYW4gYW5kIGZpbHRlciB0aGUgZGF0YXNldC4gV2UgZGlkIHRoaXMgb3ZlciBhbmQgb3ZlciwgaW4gb3JkZXIgdG8gY2hvb3NlIHRoZSB2YXJpYWJsZXMgdGhhdCBtYWRlIHRoZSBtb3N0IGFjY3VyYXRlIGFuZCBnZW5lcmFsaXphYmxlIG1vZGVsLiANCg0KDQpgYGB7ciByZWd9DQoNCiNyZWdyZXNzaW9uIG9uIHRyYWluaW5nIGRhdGENCg0KcmVnMiA8LSBsbShzYWxlX3ByaWNlIH4gLiwgZGF0YSA9IHN0X2Ryb3BfZ2VvbWV0cnkodHJhaW5fZGF0YSkgJT4lICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRwbHlyOjpzZWxlY3QodmFyaWFibGVzKSkNCg0Kc3VtbWFyeShyZWcyKQ0KDQojIyBQbG90IHJlZ3Jlc3Npb24gDQojZWZmZWN0X3Bsb3QocmVnMiwgcHJlZCA9IG51bWJlcl9vZl9iYXRocm9vbXMsIGludGVydmFsID0gVFJVRSwgcGxvdC5wb2ludHMgPSBUUlVFKQ0KcGxvdF9zdW1tcyhyZWcyLCBzY2FsZSA9IFRSVUUpDQoNCiNwcmVkaWN0aW5nIHByaWNlIGZvciB0ZXN0aW5nIGRhdGENCg0KdGVzdF9kYXRhIDwtDQogIHRlc3RfZGF0YSAlPiUNCiAgbXV0YXRlKFByaWNlLlByZWRpY3QgPSBwcmVkaWN0KHJlZzIsIHRlc3RfZGF0YSksDQogICAgICAgICBQcmljZS5FcnJvciA9IFByaWNlLlByZWRpY3QgLSBzYWxlX3ByaWNlLA0KICAgICAgICAgUHJpY2UuQWJzRXJyb3IgPSBhYnMoUHJpY2UuUHJlZGljdCAtIHNhbGVfcHJpY2UpLA0KICAgICAgICAgUHJpY2UuQVBFID0gKGFicyhQcmljZS5QcmVkaWN0IC0gc2FsZV9wcmljZSkpIC8gUHJpY2UuUHJlZGljdCkNCg0KYGBgDQoNCiMjIEVycm9yIEFuYWx5c2lzOiBBY2N1cmFjeSANCg0KVGhlIG1lYW4gYWJzb2x1dGUgZXJyb3IgKE1BRSkgZm9yIG91ciBtb2RlbCBpcyBjdXJyZW50bHkgYXBwcm94aW1hdGVseSBgJDczLDAwMGAsIGFuZCB0aGUgTWVhbiBBYnNvbHV0ZSBQZXJjZW50YWdlIEVycm9yIChNQVBFKSBzdGFuZHMgYXQgYXJvdW5kIDI4JS4gV2UgZXhwZXJpbWVudGVkIHdpdGggdmFyaW91cyB2YXJpYWJsZSBjb21iaW5hdGlvbnMgdG8gcmVkdWNlIHRoZXNlIG1ldHJpY3MsIGFuZCB3ZSBtYW5hZ2VkIHRvIGFjaGlldmUgcmVzdWx0cyBjbG9zZSB0byBgJDIwLDAwMGAgKHdpdGggYSBNQVBFIG9mIDMlKSBieSBpbmNvcnBvcmF0aW5nIHZhcmlhYmxlcyBsaWtlICJQcmljZVBlclNxRlQuIiBIb3dldmVyLCBpbmNsdWRpbmcgdGhlc2UgdmFyaWFibGVzLCB3aGljaCBhcmUgZGVyaXZlZCBmcm9tIHRoZSBwcm92aWRlZCBzYWxlX3ByaWNlLCBsZWQgdG8gYW4gb3ZlcmZpdCBtb2RlbCB0aGF0IGxhY2tlZCBnZW5lcmFsaXphYmlsaXR5IOKAkyBhIGNydWNpYWwgcmVxdWlyZW1lbnQgZm9yIG91ciBhcHBsaWNhdGlvbi4gQWRkaXRpb25hbGx5LCB0byBwcmVkaWN0IGhvbWUgcHJpY2VzIGVmZmVjdGl2ZWx5LCB3ZSBtdXN0IGF2b2lkIHVzaW5nIHZhcmlhYmxlcyBkZXJpdmVkIGZyb20gaG9tZSBwcmljZXMgdGhlbXNlbHZlcywgYXMgdGhpcyB3b3VsZCBjcmVhdGUgYSBjb252b2x1dGVkIGFuZCBzZWxmLXJlZmVyZW50aWFsIG1vZGVsLg0KDQpgYGB7cn0NCiMgdGFibGUgb2YgTUFFIGFuZCBNQVBFDQoNCiMjIEFjY3VyYWN5IC0gTWVhbiBBYnNvbHV0ZSBFcnJvcg0KTUFFIDwtIGRhdGEuZnJhbWUobWVhbih0ZXN0X2RhdGEkUHJpY2UuQWJzRXJyb3IsIG5hLnJtID0gVCkpDQpNQVBFIDwtIGRhdGEuZnJhbWUobWVhbih0ZXN0X2RhdGEkUHJpY2UuQVBFLCBuYS5ybSA9IFQpKSAjTUFQRQ0KDQoNCnJlZy5NQUUuTUFQRSA8LSBjYmluZChNQUUsIE1BUEUpICU+JQ0KICBrYWJsZShmb3JtYXQgPSAiaHRtbCIsIGNhcHRpb24gPSAiUmVncmVzc2lvbiBNQUUgJiBNQVBFIChGaWd1cmUgNS4xKSIsIGNvbC5uYW1lcyA9IGMoIk1lYW4gQWJzb2x1dGUgRXJyb3IiLCAiTWVhbiBBYnNvbHV0ZSBQZXJjZW50YWdlIEVycm9yIikpICU+JQ0KICBrYWJsZV9zdHlsaW5nKCJzdHJpcGVkIiwgZnVsbF93aWR0aCA9IEZBTFNFKQ0KDQpyZWcuTUFFLk1BUEUNCg0KYGBgDQoNCiMgR2VuZXJhbGl6YWJpbHRpeSANCg0KVXNpbmcga2ZvbGQgd2UgY2hlY2sgZ2VuZXJhbGl6YWJpbGl0eSANCg0KV2UgdXNlZCAxMDAgZm9sZHMgZm9yIGNyb3NzLXZhbGlkYXRpb24uIFRoZSBoaWdoIHZhbHVlcyBvZiBSTVNFIGluZGljYXRlIHRoYXQgdGhlIG1vZGVsIGlzIGJlaW5nIGFmZmVjdGVkIGJ5IG91dGxpZXJzIHNpZ25pZmljYW50bHkuIFRvIGFkZHJlc3MgdGhpcyBpc3N1ZSwgd2UgY29uZHVjdGVkIGFkZGl0aW9uYWwgZGF0YSBjbGVhbmluZyBhbmQgaW1wbGVtZW50ZWQgb3V0bGllciBmaWx0ZXJpbmcgdGVjaG5pcXVlcyB0byBtaW5pbWl6ZSBlcnJvcnMuIEhvd2V2ZXIsIHRoZSBhdmVyYWdlIFJNU0UgdmFsdWUgc3RpbGwgcmVtYWlucyBhYm92ZSAxMDAsMDAwLCBtYWtpbmcgaXQgcmVsYXRpdmVseSBpbmFjY3VyYXRlIGZvciBwcmVkaWN0aW5nIGhvbWUgcHJpY2VzLg0KDQpUaGUgUi1zcXVhcmVkIHN0YXRpc3RpYyByZXByZXNlbnRzIHRoZSBwcm9wb3J0aW9uIG9mIHRoZSB2YXJpYW5jZSBpbiB0aGUgc2FsZSBwcmljZXMgdGhhdCB0aGUgbW9kZWwgY2FuIGV4cGxhaW4uIEl0IHJhbmdlcyBmcm9tIDAuMyB0byAwLjksIHdpdGggYSBtZWFuIHZhbHVlIG9mIDAuNy4gQSBoaWdoZXIgUi1zcXVhcmVkIHZhbHVlIGluZGljYXRlcyBhIGJldHRlciBmaXQgb2YgdGhlIG1vZGVsIHRvIHRoZSBkYXRhLiBPbiBhdmVyYWdlLCBvdXIgbW9kZWwgY2FuIGV4cGxhaW4gYXBwcm94aW1hdGVseSA3MCUgb2YgdGhlIHZhcmlhbmNlIGluIHRoZSBkYXRhLCBpbmRpY2F0aW5nIHRoYXQgaXQgcG9zc2Vzc2VzIGEgcmVhc29uYWJsZSBsZXZlbCBvZiBnZW5lcmFsaXphYmlsaXR5Lg0KDQpgYGB7cn0NCg0KdGVzdF9kYXRhIDwtDQogIHRlc3RfZGF0YSAlPiUNCiAgZmlsdGVyKHNhbGVfcHJpY2UgPCAxMDAwMDAwMCkNCg0KIyMgSy1Gb2xkOiBHZW5lcmFsaXphYmlsaXR5IENyb3NzLVZhbGlkYXRpb24NCg0KZml0Q29udHJvbCA8LSB0cmFpbkNvbnRyb2wobWV0aG9kID0gImN2IiwgbnVtYmVyID0gMTAwKQ0Kc2V0LnNlZWQoOTk5KQ0KDQpyZWcuY3YgPC0gDQogIHRyYWluKHNhbGVfcHJpY2UgfiAuLCBkYXRhID0gc3RfZHJvcF9nZW9tZXRyeSh0ZXN0X2RhdGEpICU+JSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZHBseXI6OnNlbGVjdCh2YXJpYWJsZXMpLA0KbWV0aG9kID0gImxtIiwgdHJDb250cm9sID0gZml0Q29udHJvbCwgbmEuYWN0aW9uID0gbmEucGFzcykNCg0Kc3RhcmdhemVyKGFzLmRhdGEuZnJhbWUocmVnLmN2JHJlc2FtcGxlKSwgdHlwZT0idGV4dCIsIGRpZ2l0cz0xLCB0aXRsZT0iQ3Jvc3MgVmFsaWRhdGlvbiBSZXN1bHRzICg1LjIpIiwgb3V0ID0gIkNWLnR4dCIpICNhbGwgY3YNCg0KYGBgDQoNCk1vc3Qgb2Ygb3VyIE1BRSBzZWVtIHRvIGJlIHdpdGhpbiB0aGUgNjAsMDAwIGRvbGxhciB0byA4MCwwMDAgZG9sbGFyIHJhbmdlICh3aXRoIGEgbWVhbiBvZiA3MSwwMDApIHdoaWNoIGlzIGlsbHVzdHJhdGVkIGluIHRoZSBmb2xsb3dpbmcgaGlzdG9ncmFtLiAgDQoNCmBgYHtyfQ0KI3Bsb3R0aW5nIHRoZSBjcm9zcyB2YWxpZGF0aW9uIHN0dWZmDQoNCmdncGxvdChyZWcuY3YkcmVzYW1wbGUsIGFlcyh4PU1BRSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ICIjMmM3YmI2IikgKw0KICBsYWJzKHRpdGxlID0gIkNyb3NzIFZhbGlkYXRpb24gVGVzdHMgaW4gTWVhbiBBdmVyYWdlIEVycm9yIiwgY2FwdGlvbj0iRmlndXJlIDUuMyIpICsNCiAgcGxvdFRoZW1lKCkNCg0KYGBgDQpUaGUgcHJlZGljdGVkIGFuZCBvYnNlcnZlZCBwcmljZXMgbG9vayBldmVubHkgZGlzdHJpYnV0ZWQgYWJvdmUgYW5kIGJlbG93IHRoZSBwZXJmZWN0IHByZWRpY3Rpb24gbGluZSwgYmVsb3cgdGhlICQ1MCwwMDAgbGluZS4gT3VyIG1vZGVsIG1vdmVzIGJlbG93IHRoZSBwZXJmZWN0IHByZWRpY3Rpb24gbGluZSBiZXlvbmQgNTAsMDAwIGRvbGxhcnMgaW5kaWNhdGluZyB0aGF0IG91ciBtb2RlbCBtYXkgYmUgdW5kZXItcHJlZGljdGluZyB0aGUgdmFsdWVzIG9mIGhpZ2ggdmFsdWUgaG9tZXMuIA0KDQoNCmBgYHtyfQ0KDQp0ZXN0X2RhdGEgJT4lDQogIGRwbHlyOjpzZWxlY3QoUHJpY2UuUHJlZGljdCwgc2FsZV9wcmljZSkgJT4lDQogICAgZ2dwbG90KGFlcyhzYWxlX3ByaWNlLCBQcmljZS5QcmVkaWN0KSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBzdGF0X3Ntb290aChhZXMoc2FsZV9wcmljZSwgc2FsZV9wcmljZSksIA0KICAgICAgICAgICAgIG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UsIHNpemUgPSAxLCBjb2xvdXI9IiNkNzE5MWMiKSArIA0KICBzdGF0X3Ntb290aChhZXMoUHJpY2UuUHJlZGljdCwgc2FsZV9wcmljZSksIA0KICAgICAgICAgICAgICBtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFLCBzaXplID0gMSwgY29sb3VyPSIjMmM3YmI2IikgKw0KICBsYWJzKHRpdGxlPSJQcmVkaWN0ZWQgU2FsZSBQcmljZSBhcyBhIEZ1bmN0aW9uIG9mIE9ic2VydmVkIFByaWNlIiwNCiAgICAgICBzdWJ0aXRsZT0iUmVkIGxpbmUgcmVwcmVzZW50cyBhIHBlcmZlY3QgcHJlZGljdGlvbjsgQmx1ZSBsaW5lIHJlcHJlc2VudHMgcHJlZGljdGlvbiIsDQogICAgICAgY2FwdGlvbiA9ICJGaWd1cmUgNi4yIiwNCiAgICAgICB4ID0gIk9ic2VydmVkIFByaWNlIiwNCiAgICAgICB5ID0gIlByZWRpY3RlZCBQcmljZSIpICsNCiAgcGxvdFRoZW1lKCkrIHhsaW0oMCwgMjAwMDAwMCkgKyB5bGltKDAsIDIwMDAwMDApDQpgYGANCg0KIyBTcGF0aWFsIENvcnJlbGF0aW9ucyANCg0KYGBge3J9DQoNCiN1c2UgdGhlIHNhbWUgdGhuZyBmb3Igd2lnaHRzIGFuZCBtb3JhbnMgaSANCiNtb3ZlIHRoZSBmaWx0ZXJzIHRvIHRoaXMgY2VsIHRvbyANCg0KDQojIFNwYXRpYWwgTGFnIGZvciBwcmljZQ0KY29vcmRzIDwtIHN0X2Nvb3JkaW5hdGVzKHRlc3RfZGF0YSkgDQpuZWlnaGJvckxpc3QgPC0ga25uMm5iKGtuZWFybmVpZ2goY29vcmRzLCA1KSkNCnNwYXRpYWxXZWlnaHRzIDwtIG5iMmxpc3R3KG5laWdoYm9yTGlzdCwgc3R5bGU9IlciKQ0KdGVzdF9kYXRhJGxhZ1ByaWNlIDwtIGxhZy5saXN0dyhzcGF0aWFsV2VpZ2h0cywgdGVzdF9kYXRhJHNhbGVfcHJpY2UpDQoNCnRlc3RfZGF0YSRsYWdQcmljZUVycm9yIDwtIGxhZy5saXN0dyhzcGF0aWFsV2VpZ2h0cywgdGVzdF9kYXRhJFByaWNlLkVycm9yLCBOQU9LID0gVFJVRSkNCg0KIyBGaWx0ZXJpbmcgZ3JlYXRlciB0aGFuIDAgdmFsdWVzDQoNCnRlc3RfZGF0YV9maWx0ZXIgPC0gdGVzdF9kYXRhICU+JQ0KICBmaWx0ZXIoUHJpY2UuRXJyb3IgPiAwLCBsYWdQcmljZUVycm9yID4gMCkNCg0KZ2dwbG90KGRhdGEgPSB0ZXN0X2RhdGFfZmlsdGVyLCBhZXMobGFnUHJpY2VFcnJvciwgUHJpY2UuRXJyb3IpKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IC44NSxjb2xvdXIgPSAiYmxhY2siKSArIA0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLGNvbG91ciA9ICJyZWQiLHNpemUgPSAxLjIpICsNCiAgbGFicyh0aXRsZT0iUHJpY2UgRXJyb3JzIiwNCiAgICAgICBjYXB0aW9uID0gIkZpZ3VyZSA2LjEiKSArDQogIHBsb3RUaGVtZSgpDQoNCmBgYA0KIyMgTW9yYW4ncyBJIA0KDQpXZSBvYnNlcnZlIGEgbG93IE1vcmFuJ3MgSSB2YWx1ZSwgaW5kaWNhdGluZyB0aGF0IGVycm9ycyBhcmUgZGlzcGVyc2VkIGFjcm9zcyBvdXIgZGF0YS4gVGhpcyBzdWdnZXN0cyB0aGF0IHRoZXJlIGlzIG5vIHNpZ25pZmljYW50IGNsdXN0ZXJpbmcgb2YgZXJyb3JzLCBhIHBhdHRlcm4gdGhhdCBpcyBhbHNvIGV2aWRlbnQgaW4gdGhlIG1hcC4gQ29uc2VxdWVudGx5LCBpdCBpcyBwcm9iYWJsZSB0aGF0IGVycm9ycyBpbiBvdXIgbW9kZWwgbWF5IHN0ZW0gZnJvbSBvdGhlciBkZW1vZ3JhcGhpYyBvciBpbnRlcm5hbCB2YXJpYWJsZXMgdGhhdCB3ZSBtYXkgbm90IGhhdmUgYWNjb3VudGVkIGZvci4gVG8gaW1wcm92ZSB0aGUgYWNjdXJhY3kgb2Ygb3VyIGFuYWx5c2lzLCBmdXJ0aGVyIGludmVzdGlnYXRpb24gYW5kIGluY2x1c2lvbiBvZiB0aGVzZSBwb3RlbnRpYWxseSBvbWl0dGVkIHZhcmlhYmxlcyBtaWdodCBiZSBuZWNlc3NhcnkuDQoNCg0KYGBge3J9DQoNCiMgZm9yIG1vcmFucyAxDQoNCiMgU3BhdGlhbCBMYWcgZm9yIHByaWNlDQpjb29yZHMgPC0gc3RfY29vcmRpbmF0ZXModGVzdF9kYXRhX2ZpbHRlcikgDQpuZWlnaGJvckxpc3QgPC0ga25uMm5iKGtuZWFybmVpZ2goY29vcmRzLCA1KSkNCnNwYXRpYWxXZWlnaHRzIDwtIG5iMmxpc3R3KG5laWdoYm9yTGlzdCwgc3R5bGU9IlciKQ0KdGVzdF9kYXRhX2ZpbHRlciRsYWdQcmljZSA8LSBsYWcubGlzdHcoc3BhdGlhbFdlaWdodHMsIHRlc3RfZGF0YV9maWx0ZXIkc2FsZV9wcmljZSkNCg0KdGVzdF9kYXRhX2ZpbHRlciRsYWdQcmljZUVycm9yIDwtIGxhZy5saXN0dyhzcGF0aWFsV2VpZ2h0cywgdGVzdF9kYXRhX2ZpbHRlciRQcmljZS5FcnJvciwgTkFPSyA9IFRSVUUpDQoNCg0KDQptb3JhblRlc3QgPC0gbW9yYW4ubWMobmEub21pdCh0ZXN0X2RhdGFfZmlsdGVyJFByaWNlLkVycm9yKSwNCiAgICAgICAgICAgICAgICAgICAgICBzcGF0aWFsV2VpZ2h0cywgbnNpbSA9IDk5OSkgIA0KDQpnZ3Bsb3QoYXMuZGF0YS5mcmFtZShtb3JhblRlc3QkcmVzW2MoMTo5OTkpXSksIGFlcyhtb3JhblRlc3QkcmVzW2MoMTo5OTkpXSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjAwNSkgKw0KICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbW9yYW5UZXN0JHN0YXRpc3RpYyksIGNvbG91ciA9ICIjZDcxOTFjIixzaXplPTEpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoLTAuNSwwLjUpKSArDQogIGxhYnModGl0bGU9Ik9ic2VydmVkIGFuZCBwZXJtdXRlZCBNb3JhbidzIEkiLA0KICAgICAgIHN1YnRpdGxlPSAiT2JzZXJ2ZWQgTW9yYW4ncyBJIGluIHJlZCIsDQogICAgICAgY2FwdGlvbiA9ICJGaWd1cmUgNi4zIiwNCiAgICAgICB4PSJNb3JhbidzIEkiLA0KICAgICAgIHk9IkNvdW50IikgKw0KICBwbG90VGhlbWUoKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGEgPSB0cmFjdHMyMSwgZmlsbCA9ICJncmV5ODkiLCBjb2xvciA9ICJncmV5ODkiKSArDQogIGdlb21fc2YoZGF0YSA9IHRlc3RfZGF0YSwgYWVzKGNvbG91ciA9IHE1KFByaWNlLkFic0Vycm9yKSksIA0KICAgICAgICAgIHNob3cubGVnZW5kID0gInBvaW50Iiwgc2l6ZSA9IC43NSkgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGVlLA0KICAgICAgICAgICAgICAgICAgIGxhYmVscz1xQnIodGVzdF9kYXRhLCJQcmljZS5BYnNFcnJvciIpLA0KICAgICAgICAgICAgICAgICAgIG5hbWU9IlF1aW50aWxlXG5CcmVha3MiKSArDQogIGxhYnModGl0bGU9Ik1hcCBvZiBQcmljZSBBYnNvbHV0ZSBFcnJvcnMgZm9yIFRlc3QgU2V0IiwgDQogICAgICAgc3VidGl0bGU9ICJQaGlsYWRlbHBoaWEiLA0KICAgICAgY2FwdGlvbj0iRmlndXJlIDYuNCIpICsNCiAgbWFwVGhlbWUoKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGEgPSB0cmFjdHMyMSwgZmlsbCA9ICJncmV5ODkiLCBjb2xvciA9ICJncmV5ODkiKSArDQogIGdlb21fc2YoZGF0YSA9IHRlc3RfZGF0YSwgYWVzKGNvbG91ciA9IHE1KFByaWNlLkFic0Vycm9yKSksIA0KICAgICAgICAgIHNob3cubGVnZW5kID0gInBvaW50Iiwgc2l6ZSA9IC43NSkgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGVlLA0KICAgICAgICAgICAgICAgICAgIGxhYmVscz1xQnIodGVzdF9kYXRhLCJQcmljZS5QcmVkaWN0IiksDQogICAgICAgICAgICAgICAgICAgbmFtZT0iUXVpbnRpbGVcbkJyZWFrcyIpICsNCiAgbGFicyh0aXRsZT0iUHJlZGljdGVkIFNhbGUgUHJpY2UgZm9yIFRlc3QgU2V0IiwgDQogICAgICAgc3VidGl0bGUgPSAiUGhpbGFkZWxwaGlhIiwNCiAgICAgIGNhcHRpb249IkZpZ3VyZSA2LjQiKSArDQogIG1hcFRoZW1lKCkNCmBgYA0KIyBEaWZmZXJpbmcgQ29udGV4dHMgDQoNCkhlcmUgd2UgdHJ5IHRvIG9ic2VydmUgdHJlbmRzIG9mIGVycm9yIGFuZCBwcmVkaWN0aW9ucyB3aXRoaW4gZWFjaCBjZW5zdXMgdHJhY3QuIFdlIGNhbiBzZWUgdGhhdCB0aGUgTUFQRSBsaWVzIHdpdGhpbiBhIDAgdG8gMSByYW5nZSwgYW5kIGlzIGhpZ2hlciBpbiBjZXJ0YWluIGNlbnN1cyB0cmFjdHMgaW4gTm9ydGggUGhpbGFkZWxwaGlhLCBpbmRpY2F0aW5nIGEgcG90ZW50aWFsIG1pc3Npbmcgc3BhdGlhbCBmYWN0b3IuIA0KDQpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KDQojdGhpcyBpcyB3cm9uZyANCg0KbGlicmFyeShzZikNCg0KdGVzdF9kYXRhICU+JQ0KICBncm91cF9ieShjZW5zdXNfdHJhY3QpICU+JQ0KICBzdW1tYXJpemUoTUFQRSA9IG1lYW4oUHJpY2UuQVBFLCBuYS5ybSA9IFQpKSAlPiUNCiAgdW5ncm91cCgpIA0KDQpzdF9qb2luKHRyYWN0czIxLCB0ZXN0X2RhdGEpJT4lDQogICMgIHN0X3NmKCkgJT4lDQogICAgZ2dwbG90KCkgKyANCiAgIGdlb21fc2YoYWVzKGZpbGwgPSBNQVBFKSkgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50MigNCiAgICBsb3cgPSAiIzJjN2JiNiIsICAgIyBDb2xvciBmb3IgdmFsdWVzIDwgLTENCiAgICBtaWQgPSAiI2ZmZmZiZiIsICAjIENvbG9yIGZvciB2YWx1ZXMgYmV0d2VlbiAtMSBhbmQgKzENCiAgICBoaWdoID0gIiNkNzE5MWMiLCAgICMgQ29sb3IgZm9yIHZhbHVlcyA+ICsxDQogICAgbWlkcG9pbnQgPSAwLCAgICMgU2V0IHRoZSBtaWRwb2ludCBhdCAwDQogICAgbGltaXRzID0gYygwLCAxKSwgICMgRGVmaW5lIHRoZSBsaW1pdHMNCiAgICBicmVha3MgPSBjKDAsIDAuMiwgMC40LCAwLjgsIDEpLCAgIyBTcGVjaWZ5IGJyZWFrIHBvaW50cw0KICAgIGxhYmVscyA9IGMoIjw9IDAiLCAiMCB0byAwLjIiLCAiMC4yIHRvIDAuNCIsICIwLjQgdG8gMC44IiwgIj49IDEiKSwgICMgTGFiZWxzIGZvciBicmVha3MNCiAgICBuYW1lID0gIk1BUEUgQ2xhc3NlcyIgICMgTGVnZW5kIHRpdGxlDQogICkgICsNCiAgZ2VvbV9zZihkYXRhID0gdGVzdF9kYXRhLCBjb2xvdXIgPSAiYmxhY2siLCBhbHBoYSA9IDAuNSwgIHNob3cubGVnZW5kID0gInBvaW50Iiwgc2l6ZSA9IC41KSArDQogICAgICBsYWJzKHRpdGxlID0gIk1lYW4gdGVzdCBzZXQgTUFQRSBieSBDZW5zdXMgVHJhY3QiLA0KICAgICAgICAgICBjYXB0aW9uID0gIkZpZ3VyZSA3LjMiKSArDQogICAgICBtYXBUaGVtZSgpDQoNCmBgYA0KDQpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KIyBGaXJzdCwgam9pbiB0aGUgZGF0YSBmcmFtZXMNCmpvaW5lZF9kYXRhIDwtIHN0X2pvaW4odHJhY3RzMjEsIHRlc3RfZGF0YSkNCg0KIyBUaGVuLCBjYWxjdWxhdGUgTUFQRQ0KbWFwZV9kYXRhIDwtIGpvaW5lZF9kYXRhICU+JQ0KICBncm91cF9ieShjZW5zdXNfdHJhY3QpICU+JQ0KICBzdW1tYXJpemUoTUFQRSA9IG1lYW4oUHJpY2UuQVBFLCBuYS5ybSA9IFRSVUUpKSAlPiUNCiAgdW5ncm91cCgpDQoNCiMgQ3JlYXRlIHRoZSBwbG90DQpnZ3Bsb3QoKSArIA0KICBnZW9tX3NmKGRhdGEgPSBqb2luZWRfZGF0YSwgYWVzKGZpbGwgPSBNQVBFKSwgc2l6ZSA9IDAuNSkgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50MigNCiAgICBsb3cgPSAiIzJjN2JiNiIsDQogICAgbWlkID0gIiNmZmZmYmYiLA0KICAgIGhpZ2ggPSAiI2Q3MTkxYyIsDQogICAgbWlkcG9pbnQgPSAwLA0KICAgIGxpbWl0cyA9IGMoMCwgMSksDQogICAgYnJlYWtzID0gYygwLCAwLjIsIDAuNCwgMC44LCAxKSwNCiAgICBsYWJlbHMgPSBjKCI8PSAwIiwgIjAgdG8gMC4yIiwgIjAuMiB0byAwLjQiLCAiMC40IHRvIDAuOCIsICI+PSAxIiksDQogICAgbmFtZSA9ICJNQVBFIENsYXNzZXMiDQogICkgKw0KICBnZW9tX3NmKGRhdGEgPSBtYXBlX2RhdGEsIGNvbG9yID0gImJsYWNrIiwgYWxwaGEgPSAwLjUsIHNpemUgPSAwLjUpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJNZWFuIHRlc3Qgc2V0IE1BUEUgYnkgQ2Vuc3VzIFRyYWN0IiwNCiAgICBjYXB0aW9uID0gIkZpZ3VyZSA3LjMiDQogICkgKw0KICBtYXBUaGVtZSgpDQpgYGANCg0KYGBge3J9DQoNCnRlc3RfZGF0YSA8LQ0KICB0ZXN0X2RhdGEgJT4lDQogIGdyb3VwX2J5KGNlbnN1c190cmFjdCkgJT4lDQogIG11dGF0ZShNZWFuUHJpY2UgPSBtZWFuKHNhbGVfcHJpY2UpKQ0KDQp0ZXN0X2RhdGEgPC0NCiAgdGVzdF9kYXRhICU+JQ0KICBncm91cF9ieShjZW5zdXNfdHJhY3QpICU+JQ0KICBtdXRhdGUoTUFQRSA9IG1lYW4oUHJpY2UuQVBFKSkgJT4lDQogIGZpbHRlcihNQVBFPiAwKQ0KDQpnZ3Bsb3QodGVzdF9kYXRhKSArDQogIGdlb21fcG9pbnQoYWVzKE1lYW5QcmljZSwgTUFQRSkpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgYWVzKE1lYW5QcmljZSwgTUFQRSksIHNlID0gRkFMU0UsIGNvbG91ciA9ICJncmVlbiIpICsNCiAgbGFicyh0aXRsZSA9ICJNQVBFIGJ5IENlbnN1cyBUcmFjdCBhcyBhIGZ1bmN0aW9uIG9mIE1lYW4gUHJpY2UgYnkgQ2Vuc3VzIFRyYWN0IiwNCiAgICAgICB4ID0gIk1lYW4gSG9tZSBQcmljZSIsIHkgPSAiTUFQRSIsDQogICAgICAgY2FwdGlvbiA9ICJGaWd1cmUgNy41IikgKw0KICBwbG90VGhlbWUoKQ0KYGBgDQojIyBUaWR5Y2Vuc3VzIA0KDQojIERpc2N1c3Npb24gDQoNCkFzc2Vzc2luZyB0aGUgbW9kZWwncyBlZmZlY3RpdmVuZXNzIGlzIGEgbnVhbmNlZCB0YXNrLiBXaGlsZSBpdCBleGhpYml0cyBtb2RlcmF0ZSBnZW5lcmFsaXphYmlsaXR5LCB0aGUgcHJlc2VuY2Ugb2Ygbm90YWJseSBoaWdoIGVycm9ycyByYWlzZXMgY29uY2VybnMgYWJvdXQgaXRzIHByZWRpY3RpdmUgYWNjdXJhY3kuIE9uZSBpbnRyaWd1aW5nIGZpbmRpbmcgd2FzIHRoZSBsYWNrIG9mIHNwYXRpYWwgY29ycmVsYXRpb24gd2l0aCBzYWxlIHByaWNlcywgZGVmeWluZyB0aGUgaW50dWl0aXZlIGV4cGVjdGF0aW9uIHRoYXQgcHJvcGVydGllcyBuZWFyIGxhbmRtYXJrcyBvciBwcml2YXRlIHNjaG9vbHMgd291bGQgY29tbWFuZCBoaWdoZXIgcHJpY2VzLiBXZSB3ZXJlIGFibGUgdG8gcHJlZGljdCBhcHByb3hpbWF0ZWx5IDcwJSBvZiB0aGUgdmFyaWFuY2UgaW4gc2FsZSBwcmljZXMsIHdoaWNoIGlzIGEgcmVhc29uYWJsZSBsZXZlbCBvZiBleHBsYW5hdG9yeSBwb3dlciBmb3IgYSByZWdyZXNzaW9uIG1vZGVsLg0KDQpVbmRlcnN0YW5kaW5nIHRoZSBlcnJvcnMgaW4gcHJlZGljdGlvbnMgaXMgY29tcGxpY2F0ZWQgYnkgdGhlaXIgZGlzcGVyc2VkIG5hdHVyZSBhbmQgdGhlIGFic2VuY2Ugb2YgY2x1c3RlcmluZy4gVGhlIGxvdyBNb3JhbidzIEkgc3RhdGlzdGljIGluZGljYXRlcyBhIGxhY2sgb2Ygc3BhdGlhbCBhdXRvY29ycmVsYXRpb24sIG1ha2luZyBpdCBjaGFsbGVuZ2luZyB0byBhdHRyaWJ1dGUgZXJyb3JzIHRvIHNwZWNpZmljIGdlb2dyYXBoaWMgcGF0dGVybnMuIFRoZSBpbmZsdWVuY2Ugb2YgZGVtb2dyYXBoaWMgZmFjdG9ycywgYXMgb3V0bGluZWQgZWFybGllciwgbWF5IGJlIGNvbnRyaWJ1dGluZyB0byB0aGUgb2JzZXJ2ZWQgZXJyb3JzLg0KDQpXaGVuIGl0IGNvbWVzIHRvIHNwYXRpYWwgdmFyaWF0aW9uLCB0aGUgZGlzcGVyc2VkIGVycm9ycyBtYWtlIGl0IGRpZmZpY3VsdCB0byBpZGVudGlmeSBzcGVjaWZpYyBhcmVhcyB3aGVyZSB0aGUgbW9kZWwgcGVyZm9ybWVkIGV4Y2VwdGlvbmFsbHkgd2VsbCBvciBwb29ybHkuIEhvd2V2ZXIsIGl0J3Mgd29ydGggbm90aW5nIHRoYXQgdGhlcmUgaXMgYSBoaWdoZXIgZXJyb3IgY291bnQgaW4gU291dGggYW5kIENlbnRlciBDaXR5LCBzdWdnZXN0aW5nIHRoZSBwb3NzaWJpbGl0eSBvZiB1bmFjY291bnRlZCBzcGF0aWFsIGZhY3RvcnMgaW4gdGhvc2UgcmVnaW9ucy4NCg0KIyBQcmVkaWN0aW9uIENoYWxsZW5nZSANCg0KQWxsIHRob3NlIHRoaW5ncyBjb25zaWRlcmVkIHdlIHVzZWQgdGhlIG1vZGVsIHRvIHByZWRpY3QgaG9tZSBwcmljZXMgZm9yIHRoZSBjaGFsbGVuZ2UgZGF0YSBzZXQuIFdlIG5vdGljZSB0aGF0IHRoZSBudW1iZXIgaGFzIGdvbmUgZG93biBmcm9tIDEwMCB0byA3OSwgYnV0IHdlIGtub3cgdGhhdCB0aGlzIGlzIGFzIGEgY29uc2VxdWVuY2Ugb2Ygb3VyIGRhdGEgY2xlYW5pbmcgYW5kIHJlbW92aW5nIGRhdGEgcG9pbnRzIHdpdGggbWlzc2luZyBvciBpbmNvbXBsZXRlIHZhbHVlcyB0aGF0IGNvdWxkIG5vdCBiZSBpbXB1dGVkIChzdWNoIGFzIHllYXIgYnVpbHQpLiANCg0KYGBge3J9DQoNCiNmb3IgcHJlZGljdGlvbiANCg0KcmVnMSA8LSBsbShzYWxlX3ByaWNlIH4gLiwgZGF0YSA9IHN0X2Ryb3BfZ2VvbWV0cnkoZGF0YV9vZykgJT4lICANCiAgICAgICAgICAgICBkcGx5cjo6ZmlsdGVyKHRvUHJlZGljdCA9PSAiTU9ERUxMSU5HIikgJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkcGx5cjo6c2VsZWN0KHZhcmlhYmxlcykpDQogIA0KZGF0YV9wcmVkIDwtDQogIGRhdGFfb2cgJT4lDQogIG11dGF0ZShQcmljZS5QcmVkaWN0ID0gcHJlZGljdChyZWcxLGRhdGFfb2cpKQ0KDQpmaW5hbF9wcmVkIDwtIHN0X2Ryb3BfZ2VvbWV0cnkoZGF0YV9wcmVkKSU+JQ0KICAgZmlsdGVyKHRvUHJlZGljdD09ICJDSEFMTEVOR0UiKSU+JQ0KICBzZWxlY3QobXVzYUlELFByaWNlLlByZWRpY3QpJT4lDQogIG11dGF0ZSh0ZWFtID0gIlNhbSBhbmQgUm9zaGluaSIpDQoNCndyaXRlLmNzdihmaW5hbF9wcmVkLCAiLi9vdXRwdXRzL3NraGFyZV9yZ2FuZXNoX2NoYWxsZW5nZS5jc3YiKQ0KDQojbWFwIA0KDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IHRyYWN0czIxLCBmaWxsID0gImdyZXk4OSIsIGNvbG9yID0gImdyZXk4OSIpICsNCiAgZ2VvbV9zZihkYXRhID0gZGF0YV9wcmVkLCBhZXMoY29sb3VyID0gcTUoUHJpY2UuUHJlZGljdCkpLCANCiAgICAgICAgICBzaG93LmxlZ2VuZCA9ICJwb2ludCIsIHNpemUgPSAuNzUpICsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlZSwNCiAgICAgICAgICAgICAgICAgICBsYWJlbHM9cUJyKGRhdGFfcHJlZCwiUHJpY2UuUHJlZGljdCIpLA0KICAgICAgICAgICAgICAgICAgIG5hbWU9IlF1aW50aWxlXG5CcmVha3MiKSArDQogIGxhYnModGl0bGU9IlByZWRpY3RlZCBTYWxlIFByaWNlIGZvciBhbGwgRGF0YSIsIA0KICAgICAgIHN1YnRpdGxlID0gIlBoaWxhZGVscGhpYSIpICsNCiAgbWFwVGhlbWUoKQ0KDQoNCmBgYA0KDQojIENvbmNsdXNpb24NCg0KV2Ugd291bGQgcmVjb21tZW5kIHRoaXMgbW9kZWwgdG8gWmlsbG93IGlmIHRoZSBlcnJvcnMgd2VyZW4ndCBzbyBoaWdoLiBUaGUgbW9kZWwgcGVyZm9ybXMgd2VsbCBpbiBtYW55IGNvbnRleHRzIGJ1dCBpZiB0aW1lIHdhcyBubyBib3VuZCwgd2Ugd291bGQgaGF2ZSByZWFsbHkgbGlrZWQgdG8gZXhwbG9yZSBtb3JlIHZhcmlhYmxlcyBhbmQgcmVmaW5lIGl0IG1vcmUgdG8gbWluaW1pemUgdGhlIGVycm9ycyBiZWZvcmUgc2hhcmluZyB0aGUgbW9kZWwuIA0KDQo=